PostgreSQL Source Code  git master
tablecmds.c File Reference
#include "postgres.h"
#include "access/attmap.h"
#include "access/genam.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_publication_rel.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "catalog/toasting.h"
#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "commands/user.h"
#include "commands/vacuum.h"
#include "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_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)
 
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)
 
static Oid transformFkeyCheckAttrs (Relation pkrel, int numattrs, int16 *attnums, Oid *opclasses)
 
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)
 
static void CheckAlterTableIsSafe (Relation rel)
 
static void ATController (AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATPrepCmd (List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATRewriteCatalogs (List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATExecCmd (List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
 
static AlterTableCmdATParseTransformCmd (List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
 
static void ATRewriteTables (AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static void ATRewriteTable (AlteredTableInfo *tab, Oid OIDNewHeap, 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 void ATPrepDropNotNull (Relation rel, bool recurse, bool recursing)
 
static ObjectAddress ATExecDropNotNull (Relation rel, const char *colName, LOCKMODE lockmode)
 
static void ATPrepSetNotNull (List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static ObjectAddress ATExecSetNotNull (AlteredTableInfo *tab, Relation rel, const char *colName, LOCKMODE lockmode)
 
static void ATExecCheckNotNull (AlteredTableInfo *tab, 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 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 ATAddCheckConstraint (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)
 
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)
 
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 recursing, bool missing_ok, LOCKMODE lockmode)
 
static void ATPrepAlterColumnType (List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
static bool ATColumnChangeRequiresRewrite (Node *expr, AttrNumber varattno)
 
static ObjectAddress ATExecAlterColumnType (AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
 
static void RememberAllDependentForRebuilding (AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
 
static void RememberConstraintForRebuilding (Oid conoid, AlteredTableInfo *tab)
 
static void RememberIndexForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
static void RememberStatisticsForRebuilding (Oid stxoid, AlteredTableInfo *tab)
 
static void ATPostAlterTypeCleanup (List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
 
static void ATPostAlterTypeParse (Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
 
static void RebuildConstraintComment (AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
 
static void TryReuseIndex (Oid oldId, IndexStmt *stmt)
 
static void TryReuseForeignKey (Oid oldId, Constraint *con)
 
static ObjectAddress ATExecAlterColumnGenericOptions (Relation rel, const char *colName, List *options, LOCKMODE lockmode)
 
static void change_owner_fix_column_acls (Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
 
static void change_owner_recurse_to_sequences (Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
 
static ObjectAddress ATExecClusterOn (Relation rel, const char *indexName, LOCKMODE lockmode)
 
static void ATExecDropCluster (Relation rel, LOCKMODE lockmode)
 
static void ATPrepSetAccessMethod (AlteredTableInfo *tab, Relation rel, const char *amname)
 
static void ATExecSetAccessMethodNoStorage (Relation rel, Oid newAccessMethodId)
 
static 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 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 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 Relation createPartitionTable (RangeVar *newPartName, Relation modelRel, AlterTableUtilityContext *context)
 
static void moveMergedTablesRows (Relation rel, List *mergingPartitionsList, Relation newPartRel)
 

Variables

static Liston_commits = NIL
 
static const struct dropmsgstrings dropmsgstringarray []
 

Macro Definition Documentation

◆ AT_NUM_PASSES

#define AT_NUM_PASSES   (AT_PASS_MISC + 1)

Definition at line 164 of file tablecmds.c.

◆ ATT_COMPOSITE_TYPE

#define ATT_COMPOSITE_TYPE   0x0010

Definition at line 329 of file tablecmds.c.

◆ ATT_FOREIGN_TABLE

#define ATT_FOREIGN_TABLE   0x0020

Definition at line 330 of file tablecmds.c.

◆ ATT_INDEX

#define ATT_INDEX   0x0008

Definition at line 328 of file tablecmds.c.

◆ ATT_MATVIEW

#define ATT_MATVIEW   0x0004

Definition at line 327 of file tablecmds.c.

◆ ATT_PARTITIONED_INDEX

#define ATT_PARTITIONED_INDEX   0x0040

Definition at line 331 of file tablecmds.c.

◆ ATT_SEQUENCE

#define ATT_SEQUENCE   0x0080

Definition at line 332 of file tablecmds.c.

◆ ATT_TABLE

#define ATT_TABLE   0x0001

Definition at line 325 of file tablecmds.c.

◆ ATT_VIEW

#define ATT_VIEW   0x0002

Definition at line 326 of file tablecmds.c.

◆ child_dependency_type

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

Definition at line 353 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_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_INDEX, /* re-add existing indexes */
154  AT_PASS_OLD_CONSTR, /* re-add existing constraints */
155  /* We could support a RENAME COLUMN pass here, but not currently used */
156  AT_PASS_ADD_CONSTR, /* ADD constraints (initial examination) */
157  AT_PASS_COL_ATTRS, /* set column attributes, eg NOT NULL */
158  AT_PASS_ADD_INDEXCONSTR, /* ADD index-based constraints */
159  AT_PASS_ADD_INDEX, /* ADD indexes */
160  AT_PASS_ADD_OTHERCONSTR, /* ADD other constraints, defaults */
161  AT_PASS_MISC, /* other stuff */
AlterTablePass
Definition: tablecmds.c:147
@ AT_PASS_ADD_CONSTR
Definition: tablecmds.c:156
@ AT_PASS_ADD_OTHERCONSTR
Definition: tablecmds.c:160
@ AT_PASS_OLD_CONSTR
Definition: tablecmds.c:154
@ AT_PASS_ADD_COL
Definition: tablecmds.c:151
@ AT_PASS_OLD_INDEX
Definition: tablecmds.c:153
@ AT_PASS_ALTER_TYPE
Definition: tablecmds.c:150
@ AT_PASS_ADD_INDEXCONSTR
Definition: tablecmds.c:158
@ AT_PASS_DROP
Definition: tablecmds.c:149
@ AT_PASS_MISC
Definition: tablecmds.c:161
@ AT_PASS_COL_ATTRS
Definition: tablecmds.c:157
@ AT_PASS_SET_EXPRESSION
Definition: tablecmds.c:152
@ AT_PASS_ADD_INDEX
Definition: tablecmds.c:159
@ 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 7484 of file tablecmds.c.

7485 {
7486  ObjectAddress myself,
7487  referenced;
7488 
7489  /* We know the default collation is pinned, so don't bother recording it */
7490  if (OidIsValid(collid) && collid != DEFAULT_COLLATION_OID)
7491  {
7492  myself.classId = RelationRelationId;
7493  myself.objectId = relid;
7494  myself.objectSubId = attnum;
7495  referenced.classId = CollationRelationId;
7496  referenced.objectId = collid;
7497  referenced.objectSubId = 0;
7498  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7499  }
7500 }
#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:46

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

7467 {
7468  ObjectAddress myself,
7469  referenced;
7470 
7471  myself.classId = RelationRelationId;
7472  myself.objectId = relid;
7473  myself.objectSubId = attnum;
7474  referenced.classId = TypeRelationId;
7475  referenced.objectId = typid;
7476  referenced.objectSubId = 0;
7477  recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
7478 }

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

Definition at line 10069 of file tablecmds.c.

10077 {
10078  ObjectAddress address;
10079  Oid constrOid;
10080  char *conname;
10081  bool conislocal;
10082  int coninhcount;
10083  bool connoinherit;
10084  Oid deleteTriggerOid,
10085  updateTriggerOid;
10086 
10087  /*
10088  * Verify relkind for each referenced partition. At the top level, this
10089  * is redundant with a previous check, but we need it when recursing.
10090  */
10091  if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10092  pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10093  ereport(ERROR,
10094  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10095  errmsg("referenced relation \"%s\" is not a table",
10096  RelationGetRelationName(pkrel))));
10097 
10098  /*
10099  * Caller supplies us with a constraint name; however, it may be used in
10100  * this partition, so come up with a different one in that case.
10101  */
10103  RelationGetRelid(rel),
10104  fkconstraint->conname))
10107  "fkey",
10108  RelationGetNamespace(rel), NIL);
10109  else
10110  conname = fkconstraint->conname;
10111 
10112  if (OidIsValid(parentConstr))
10113  {
10114  conislocal = false;
10115  coninhcount = 1;
10116  connoinherit = false;
10117  }
10118  else
10119  {
10120  conislocal = true;
10121  coninhcount = 0;
10122 
10123  /*
10124  * always inherit for partitioned tables, never for legacy inheritance
10125  */
10126  connoinherit = rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE;
10127  }
10128 
10129  /*
10130  * Record the FK constraint in pg_constraint.
10131  */
10132  constrOid = CreateConstraintEntry(conname,
10133  RelationGetNamespace(rel),
10134  CONSTRAINT_FOREIGN,
10135  fkconstraint->deferrable,
10136  fkconstraint->initdeferred,
10137  fkconstraint->initially_valid,
10138  parentConstr,
10139  RelationGetRelid(rel),
10140  fkattnum,
10141  numfks,
10142  numfks,
10143  InvalidOid, /* not a domain constraint */
10144  indexOid,
10145  RelationGetRelid(pkrel),
10146  pkattnum,
10147  pfeqoperators,
10148  ppeqoperators,
10149  ffeqoperators,
10150  numfks,
10151  fkconstraint->fk_upd_action,
10152  fkconstraint->fk_del_action,
10153  fkdelsetcols,
10154  numfkdelsetcols,
10155  fkconstraint->fk_matchtype,
10156  NULL, /* no exclusion constraint */
10157  NULL, /* no check constraint */
10158  NULL,
10159  conislocal, /* islocal */
10160  coninhcount, /* inhcount */
10161  connoinherit, /* conNoInherit */
10162  false); /* is_internal */
10163 
10164  ObjectAddressSet(address, ConstraintRelationId, constrOid);
10165 
10166  /*
10167  * Mark the child constraint as part of the parent constraint; it must not
10168  * be dropped on its own. (This constraint is deleted when the partition
10169  * is detached, but a special check needs to occur that the partition
10170  * contains no referenced values.)
10171  */
10172  if (OidIsValid(parentConstr))
10173  {
10174  ObjectAddress referenced;
10175 
10176  ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10177  recordDependencyOn(&address, &referenced, DEPENDENCY_INTERNAL);
10178  }
10179 
10180  /* make new constraint visible, in case we add more */
10182 
10183  /*
10184  * Create the action triggers that enforce the constraint.
10185  */
10187  fkconstraint,
10188  constrOid, indexOid,
10189  parentDelTrigger, parentUpdTrigger,
10190  &deleteTriggerOid, &updateTriggerOid);
10191 
10192  /*
10193  * If the referenced table is partitioned, recurse on ourselves to handle
10194  * each partition. We need one pg_constraint row created for each
10195  * partition in addition to the pg_constraint row for the parent table.
10196  */
10197  if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10198  {
10199  PartitionDesc pd = RelationGetPartitionDesc(pkrel, true);
10200 
10201  for (int i = 0; i < pd->nparts; i++)
10202  {
10203  Relation partRel;
10204  AttrMap *map;
10205  AttrNumber *mapped_pkattnum;
10206  Oid partIndexId;
10207 
10208  partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10209 
10210  /*
10211  * Map the attribute numbers in the referenced side of the FK
10212  * definition to match the partition's column layout.
10213  */
10215  RelationGetDescr(pkrel),
10216  false);
10217  if (map)
10218  {
10219  mapped_pkattnum = palloc(sizeof(AttrNumber) * numfks);
10220  for (int j = 0; j < numfks; j++)
10221  mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10222  }
10223  else
10224  mapped_pkattnum = pkattnum;
10225 
10226  /* do the deed */
10227  partIndexId = index_get_partition(partRel, indexOid);
10228  if (!OidIsValid(partIndexId))
10229  elog(ERROR, "index for %u not found in partition %s",
10230  indexOid, RelationGetRelationName(partRel));
10231  addFkRecurseReferenced(wqueue, fkconstraint, rel, partRel,
10232  partIndexId, constrOid, numfks,
10233  mapped_pkattnum, fkattnum,
10234  pfeqoperators, ppeqoperators, ffeqoperators,
10235  numfkdelsetcols, fkdelsetcols,
10236  old_check_ok,
10237  deleteTriggerOid, updateTriggerOid);
10238 
10239  /* Done -- clean up (but keep the lock) */
10240  table_close(partRel, NoLock);
10241  if (map)
10242  {
10243  pfree(mapped_pkattnum);
10244  free_attrmap(map);
10245  }
10246  }
10247  }
10248 
10249  return address;
10250 }
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:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#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:1521
void * palloc(Size size)
Definition: mcxt.c:1317
#define ObjectAddressSet(addr, class_id, object_id)
Definition: objectaddress.h:40
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition: partdesc.c:71
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 is_internal)
Definition: pg_constraint.c:48
@ 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:2739
char fk_upd_action
Definition: parsenodes.h:2770
char fk_matchtype
Definition: parsenodes.h:2769
bool initially_valid
Definition: parsenodes.h:2741
bool deferrable
Definition: parsenodes.h:2738
char * conname
Definition: parsenodes.h:2737
char fk_del_action
Definition: parsenodes.h:2771
List * fk_attrs
Definition: parsenodes.h:2767
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:9403
static void createForeignKeyActionTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:12273
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)
Definition: tablecmds.c:10069
void CommandCounterIncrement(void)
Definition: xact.c:1098

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

Definition at line 10289 of file tablecmds.c.

10296 {
10297  Oid insertTriggerOid,
10298  updateTriggerOid;
10299 
10300  Assert(OidIsValid(parentConstr));
10301 
10302  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10303  ereport(ERROR,
10304  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10305  errmsg("foreign key constraints are not supported on foreign tables")));
10306 
10307  /*
10308  * Add the check triggers to it and, if necessary, schedule it to be
10309  * checked in Phase 3.
10310  *
10311  * If the relation is partitioned, drill down to do it to its partitions.
10312  */
10314  RelationGetRelid(pkrel),
10315  fkconstraint,
10316  parentConstr,
10317  indexOid,
10318  parentInsTrigger, parentUpdTrigger,
10319  &insertTriggerOid, &updateTriggerOid);
10320 
10321  if (rel->rd_rel->relkind == RELKIND_RELATION)
10322  {
10323  /*
10324  * Tell Phase 3 to check that the constraint is satisfied by existing
10325  * rows. We can skip this during table creation, when requested
10326  * explicitly by specifying NOT VALID in an ADD FOREIGN KEY command,
10327  * and when we're recreating a constraint following a SET DATA TYPE
10328  * operation that did not impugn its validity.
10329  */
10330  if (wqueue && !old_check_ok && !fkconstraint->skip_validation)
10331  {
10332  NewConstraint *newcon;
10333  AlteredTableInfo *tab;
10334 
10335  tab = ATGetQueueEntry(wqueue, rel);
10336 
10337  newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
10338  newcon->name = get_constraint_name(parentConstr);
10339  newcon->contype = CONSTR_FOREIGN;
10340  newcon->refrelid = RelationGetRelid(pkrel);
10341  newcon->refindid = indexOid;
10342  newcon->conid = parentConstr;
10343  newcon->qual = (Node *) fkconstraint;
10344 
10345  tab->constraints = lappend(tab->constraints, newcon);
10346  }
10347  }
10348  else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10349  {
10350  PartitionDesc pd = RelationGetPartitionDesc(rel, true);
10351  Relation trigrel;
10352 
10353  /*
10354  * Triggers of the foreign keys will be manipulated a bunch of times
10355  * in the loop below. To avoid repeatedly opening/closing the trigger
10356  * catalog relation, we open it here and pass it to the subroutines
10357  * called below.
10358  */
10359  trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10360 
10361  /*
10362  * Recurse to take appropriate action on each partition; either we
10363  * find an existing constraint to reparent to ours, or we create a new
10364  * one.
10365  */
10366  for (int i = 0; i < pd->nparts; i++)
10367  {
10368  Oid partitionId = pd->oids[i];
10369  Relation partition = table_open(partitionId, lockmode);
10370  List *partFKs;
10371  AttrMap *attmap;
10372  AttrNumber mapped_fkattnum[INDEX_MAX_KEYS];
10373  bool attached;
10374  char *conname;
10375  Oid constrOid;
10376  ObjectAddress address,
10377  referenced;
10378  ListCell *cell;
10379 
10380  CheckAlterTableIsSafe(partition);
10381 
10382  attmap = build_attrmap_by_name(RelationGetDescr(partition),
10383  RelationGetDescr(rel),
10384  false);
10385  for (int j = 0; j < numfks; j++)
10386  mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
10387 
10388  /* Check whether an existing constraint can be repurposed */
10389  partFKs = copyObject(RelationGetFKeyList(partition));
10390  attached = false;
10391  foreach(cell, partFKs)
10392  {
10393  ForeignKeyCacheInfo *fk;
10394 
10395  fk = lfirst_node(ForeignKeyCacheInfo, cell);
10397  partitionId,
10398  parentConstr,
10399  numfks,
10400  mapped_fkattnum,
10401  pkattnum,
10402  pfeqoperators,
10403  insertTriggerOid,
10404  updateTriggerOid,
10405  trigrel))
10406  {
10407  attached = true;
10408  break;
10409  }
10410  }
10411  if (attached)
10412  {
10413  table_close(partition, NoLock);
10414  continue;
10415  }
10416 
10417  /*
10418  * No luck finding a good constraint to reuse; create our own.
10419  */
10421  RelationGetRelid(partition),
10422  fkconstraint->conname))
10423  conname = ChooseConstraintName(RelationGetRelationName(partition),
10425  "fkey",
10426  RelationGetNamespace(partition), NIL);
10427  else
10428  conname = fkconstraint->conname;
10429  constrOid =
10430  CreateConstraintEntry(conname,
10431  RelationGetNamespace(partition),
10432  CONSTRAINT_FOREIGN,
10433  fkconstraint->deferrable,
10434  fkconstraint->initdeferred,
10435  fkconstraint->initially_valid,
10436  parentConstr,
10437  partitionId,
10438  mapped_fkattnum,
10439  numfks,
10440  numfks,
10441  InvalidOid,
10442  indexOid,
10443  RelationGetRelid(pkrel),
10444  pkattnum,
10445  pfeqoperators,
10446  ppeqoperators,
10447  ffeqoperators,
10448  numfks,
10449  fkconstraint->fk_upd_action,
10450  fkconstraint->fk_del_action,
10451  fkdelsetcols,
10452  numfkdelsetcols,
10453  fkconstraint->fk_matchtype,
10454  NULL,
10455  NULL,
10456  NULL,
10457  false,
10458  1,
10459  false,
10460  false);
10461 
10462  /*
10463  * Give this constraint partition-type dependencies on the parent
10464  * constraint as well as the table.
10465  */
10466  ObjectAddressSet(address, ConstraintRelationId, constrOid);
10467  ObjectAddressSet(referenced, ConstraintRelationId, parentConstr);
10468  recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10469  ObjectAddressSet(referenced, RelationRelationId, partitionId);
10470  recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10471 
10472  /* Make all this visible before recursing */
10474 
10475  /* call ourselves to finalize the creation and we're done */
10476  addFkRecurseReferencing(wqueue, fkconstraint, partition, pkrel,
10477  indexOid,
10478  constrOid,
10479  numfks,
10480  pkattnum,
10481  mapped_fkattnum,
10482  pfeqoperators,
10483  ppeqoperators,
10484  ffeqoperators,
10485  numfkdelsetcols,
10486  fkdelsetcols,
10487  old_check_ok,
10488  lockmode,
10489  insertTriggerOid,
10490  updateTriggerOid);
10491 
10492  table_close(partition, NoLock);
10493  }
10494 
10495  table_close(trigrel, RowExclusiveLock);
10496  }
10497 }
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:1347
#define copyObject(obj)
Definition: nodes.h:224
@ CONSTR_FOREIGN
Definition: parsenodes.h:2714
#define INDEX_MAX_KEYS
#define lfirst_node(type, lc)
Definition: pg_list.h:176
List * RelationGetFKeyList(Relation relation)
Definition: relcache.c:4692
List * constraints
Definition: tablecmds.c:185
bool skip_validation
Definition: parsenodes.h:2740
Definition: pg_list.h:54
char * name
Definition: tablecmds.c:214
ConstrType contype
Definition: tablecmds.c:215
Node * qual
Definition: tablecmds.c:219
Definition: nodes.h:129
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition: tablecmds.c:6361
static void CheckAlterTableIsSafe(Relation rel)
Definition: tablecmds.c:4277
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:11017
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)
Definition: tablecmds.c:10289
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
Definition: tablecmds.c:12408

References Assert, ATGetQueueEntry(), AttrMap::attnums, build_attrmap_by_name(), CheckAlterTableIsSafe(), ChooseConstraintName(), ChooseForeignKeyConstraintNameAddition(), CommandCounterIncrement(), NewConstraint::conid, Constraint::conname, CONSTR_FOREIGN, CONSTRAINT_RELATION, ConstraintNameIsUsed(), AlteredTableInfo::constraints, NewConstraint::contype, 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, 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 6395 of file tablecmds.c.

6396 {
6397  switch (cmdtype)
6398  {
6399  case AT_AddColumn:
6400  case AT_AddColumnToView:
6401  return "ADD COLUMN";
6402  case AT_ColumnDefault:
6404  return "ALTER COLUMN ... SET DEFAULT";
6405  case AT_DropNotNull:
6406  return "ALTER COLUMN ... DROP NOT NULL";
6407  case AT_SetNotNull:
6408  return "ALTER COLUMN ... SET NOT NULL";
6409  case AT_SetExpression:
6410  return "ALTER COLUMN ... SET EXPRESSION";
6411  case AT_DropExpression:
6412  return "ALTER COLUMN ... DROP EXPRESSION";
6413  case AT_CheckNotNull:
6414  return NULL; /* not real grammar */
6415  case AT_SetStatistics:
6416  return "ALTER COLUMN ... SET STATISTICS";
6417  case AT_SetOptions:
6418  return "ALTER COLUMN ... SET";
6419  case AT_ResetOptions:
6420  return "ALTER COLUMN ... RESET";
6421  case AT_SetStorage:
6422  return "ALTER COLUMN ... SET STORAGE";
6423  case AT_SetCompression:
6424  return "ALTER COLUMN ... SET COMPRESSION";
6425  case AT_DropColumn:
6426  return "DROP COLUMN";
6427  case AT_AddIndex:
6428  case AT_ReAddIndex:
6429  return NULL; /* not real grammar */
6430  case AT_AddConstraint:
6431  case AT_ReAddConstraint:
6433  case AT_AddIndexConstraint:
6434  return "ADD CONSTRAINT";
6435  case AT_AlterConstraint:
6436  return "ALTER CONSTRAINT";
6437  case AT_ValidateConstraint:
6438  return "VALIDATE CONSTRAINT";
6439  case AT_DropConstraint:
6440  return "DROP CONSTRAINT";
6441  case AT_ReAddComment:
6442  return NULL; /* not real grammar */
6443  case AT_AlterColumnType:
6444  return "ALTER COLUMN ... SET DATA TYPE";
6446  return "ALTER COLUMN ... OPTIONS";
6447  case AT_ChangeOwner:
6448  return "OWNER TO";
6449  case AT_ClusterOn:
6450  return "CLUSTER ON";
6451  case AT_DropCluster:
6452  return "SET WITHOUT CLUSTER";
6453  case AT_SetAccessMethod:
6454  return "SET ACCESS METHOD";
6455  case AT_SetLogged:
6456  return "SET LOGGED";
6457  case AT_SetUnLogged:
6458  return "SET UNLOGGED";
6459  case AT_DropOids:
6460  return "SET WITHOUT OIDS";
6461  case AT_SetTableSpace:
6462  return "SET TABLESPACE";
6463  case AT_SetRelOptions:
6464  return "SET";
6465  case AT_ResetRelOptions:
6466  return "RESET";
6467  case AT_ReplaceRelOptions:
6468  return NULL; /* not real grammar */
6469  case AT_EnableTrig:
6470  return "ENABLE TRIGGER";
6471  case AT_EnableAlwaysTrig:
6472  return "ENABLE ALWAYS TRIGGER";
6473  case AT_EnableReplicaTrig:
6474  return "ENABLE REPLICA TRIGGER";
6475  case AT_DisableTrig:
6476  return "DISABLE TRIGGER";
6477  case AT_EnableTrigAll:
6478  return "ENABLE TRIGGER ALL";
6479  case AT_DisableTrigAll:
6480  return "DISABLE TRIGGER ALL";
6481  case AT_EnableTrigUser:
6482  return "ENABLE TRIGGER USER";
6483  case AT_DisableTrigUser:
6484  return "DISABLE TRIGGER USER";
6485  case AT_EnableRule:
6486  return "ENABLE RULE";
6487  case AT_EnableAlwaysRule:
6488  return "ENABLE ALWAYS RULE";
6489  case AT_EnableReplicaRule:
6490  return "ENABLE REPLICA RULE";
6491  case AT_DisableRule:
6492  return "DISABLE RULE";
6493  case AT_AddInherit:
6494  return "INHERIT";
6495  case AT_DropInherit:
6496  return "NO INHERIT";
6497  case AT_AddOf:
6498  return "OF";
6499  case AT_DropOf:
6500  return "NOT OF";
6501  case AT_ReplicaIdentity:
6502  return "REPLICA IDENTITY";
6503  case AT_EnableRowSecurity:
6504  return "ENABLE ROW SECURITY";
6505  case AT_DisableRowSecurity:
6506  return "DISABLE ROW SECURITY";
6507  case AT_ForceRowSecurity:
6508  return "FORCE ROW SECURITY";
6509  case AT_NoForceRowSecurity:
6510  return "NO FORCE ROW SECURITY";
6511  case AT_GenericOptions:
6512  return "OPTIONS";
6513  case AT_AttachPartition:
6514  return "ATTACH PARTITION";
6515  case AT_DetachPartition:
6516  return "DETACH PARTITION";
6518  return "DETACH PARTITION ... FINALIZE";
6519  case AT_SplitPartition:
6520  return "SPLIT PARTITION";
6521  case AT_MergePartitions:
6522  return "MERGE PARTITIONS";
6523  case AT_AddIdentity:
6524  return "ALTER COLUMN ... ADD IDENTITY";
6525  case AT_SetIdentity:
6526  return "ALTER COLUMN ... SET";
6527  case AT_DropIdentity:
6528  return "ALTER COLUMN ... DROP IDENTITY";
6529  case AT_ReAddStatistics:
6530  return NULL; /* not real grammar */
6531  }
6532 
6533  return NULL;
6534 }
@ AT_AddIndexConstraint
Definition: parsenodes.h:2375
@ AT_MergePartitions
Definition: parsenodes.h:2417
@ AT_DropOf
Definition: parsenodes.h:2406
@ AT_CheckNotNull
Definition: parsenodes.h:2361
@ AT_SetOptions
Definition: parsenodes.h:2363
@ AT_DropIdentity
Definition: parsenodes.h:2420
@ AT_DisableTrigUser
Definition: parsenodes.h:2398
@ AT_DropNotNull
Definition: parsenodes.h:2357
@ AT_AddOf
Definition: parsenodes.h:2405
@ AT_ResetOptions
Definition: parsenodes.h:2364
@ AT_ReplicaIdentity
Definition: parsenodes.h:2407
@ AT_ReplaceRelOptions
Definition: parsenodes.h:2390
@ AT_EnableRowSecurity
Definition: parsenodes.h:2408
@ AT_AddColumnToView
Definition: parsenodes.h:2354
@ AT_ResetRelOptions
Definition: parsenodes.h:2389
@ AT_EnableReplicaTrig
Definition: parsenodes.h:2393
@ AT_DropOids
Definition: parsenodes.h:2385
@ AT_SetIdentity
Definition: parsenodes.h:2419
@ AT_ReAddStatistics
Definition: parsenodes.h:2421
@ AT_SetUnLogged
Definition: parsenodes.h:2384
@ AT_DisableTrig
Definition: parsenodes.h:2394
@ AT_SetCompression
Definition: parsenodes.h:2366
@ AT_DropExpression
Definition: parsenodes.h:2360
@ AT_AddIndex
Definition: parsenodes.h:2368
@ AT_EnableReplicaRule
Definition: parsenodes.h:2401
@ AT_ReAddIndex
Definition: parsenodes.h:2369
@ AT_DropConstraint
Definition: parsenodes.h:2376
@ AT_SetNotNull
Definition: parsenodes.h:2358
@ AT_ClusterOn
Definition: parsenodes.h:2381
@ AT_AddIdentity
Definition: parsenodes.h:2418
@ AT_ForceRowSecurity
Definition: parsenodes.h:2410
@ AT_EnableAlwaysRule
Definition: parsenodes.h:2400
@ AT_SetAccessMethod
Definition: parsenodes.h:2386
@ AT_AlterColumnType
Definition: parsenodes.h:2378
@ AT_DetachPartitionFinalize
Definition: parsenodes.h:2415
@ AT_AddInherit
Definition: parsenodes.h:2403
@ AT_ReAddDomainConstraint
Definition: parsenodes.h:2372
@ AT_EnableTrig
Definition: parsenodes.h:2391
@ AT_DropColumn
Definition: parsenodes.h:2367
@ AT_ReAddComment
Definition: parsenodes.h:2377
@ AT_AlterColumnGenericOptions
Definition: parsenodes.h:2379
@ AT_DisableTrigAll
Definition: parsenodes.h:2396
@ AT_EnableRule
Definition: parsenodes.h:2399
@ AT_NoForceRowSecurity
Definition: parsenodes.h:2411
@ AT_DetachPartition
Definition: parsenodes.h:2414
@ AT_SetStatistics
Definition: parsenodes.h:2362
@ AT_AttachPartition
Definition: parsenodes.h:2413
@ AT_AddConstraint
Definition: parsenodes.h:2370
@ AT_DropInherit
Definition: parsenodes.h:2404
@ AT_EnableAlwaysTrig
Definition: parsenodes.h:2392
@ AT_SetLogged
Definition: parsenodes.h:2383
@ AT_SetStorage
Definition: parsenodes.h:2365
@ AT_DisableRule
Definition: parsenodes.h:2402
@ AT_DisableRowSecurity
Definition: parsenodes.h:2409
@ AT_SetRelOptions
Definition: parsenodes.h:2388
@ AT_ChangeOwner
Definition: parsenodes.h:2380
@ AT_EnableTrigUser
Definition: parsenodes.h:2397
@ AT_SetExpression
Definition: parsenodes.h:2359
@ AT_ReAddConstraint
Definition: parsenodes.h:2371
@ AT_SetTableSpace
Definition: parsenodes.h:2387
@ AT_GenericOptions
Definition: parsenodes.h:2412
@ AT_ColumnDefault
Definition: parsenodes.h:2355
@ AT_CookedColumnDefault
Definition: parsenodes.h:2356
@ AT_AlterConstraint
Definition: parsenodes.h:2373
@ AT_EnableTrigAll
Definition: parsenodes.h:2395
@ AT_SplitPartition
Definition: parsenodes.h:2416
@ AT_DropCluster
Definition: parsenodes.h:2382
@ AT_ValidateConstraint
Definition: parsenodes.h:2374
@ AT_AddColumn
Definition: parsenodes.h:2353

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_CheckNotNull, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_MergePartitions, AT_NoForceRowSecurity, AT_ReAddComment, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReAddIndex, AT_ReAddStatistics, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, AT_SplitPartition, and AT_ValidateConstraint.

Referenced by ATSimplePermissions().

◆ AlterIndexNamespaces()

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

Definition at line 17222 of file tablecmds.c.

17224 {
17225  List *indexList;
17226  ListCell *l;
17227 
17228  indexList = RelationGetIndexList(rel);
17229 
17230  foreach(l, indexList)
17231  {
17232  Oid indexOid = lfirst_oid(l);
17233  ObjectAddress thisobj;
17234 
17235  thisobj.classId = RelationRelationId;
17236  thisobj.objectId = indexOid;
17237  thisobj.objectSubId = 0;
17238 
17239  /*
17240  * Note: currently, the index will not have its own dependency on the
17241  * namespace, so we don't need to do changeDependencyFor(). There's no
17242  * row type in pg_type, either.
17243  *
17244  * XXX this objsMoved test may be pointless -- surely we have a single
17245  * dependency link from a relation to each index?
17246  */
17247  if (!object_address_present(&thisobj, objsMoved))
17248  {
17249  AlterRelationNamespaceInternal(classRel, indexOid,
17250  oldNspOid, newNspOid,
17251  false, objsMoved);
17252  add_exact_object_address(&thisobj, objsMoved);
17253  }
17254  }
17255 
17256  list_free(indexList);
17257 }
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
Definition: dependency.c:2593
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
Definition: dependency.c:2533
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:4801
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
Definition: tablecmds.c:17152

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

17156 {
17157  HeapTuple classTup;
17158  Form_pg_class classForm;
17159  ObjectAddress thisobj;
17160  bool already_done = false;
17161 
17162  classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid));
17163  if (!HeapTupleIsValid(classTup))
17164  elog(ERROR, "cache lookup failed for relation %u", relOid);
17165  classForm = (Form_pg_class) GETSTRUCT(classTup);
17166 
17167  Assert(classForm->relnamespace == oldNspOid);
17168 
17169  thisobj.classId = RelationRelationId;
17170  thisobj.objectId = relOid;
17171  thisobj.objectSubId = 0;
17172 
17173  /*
17174  * If the object has already been moved, don't move it again. If it's
17175  * already in the right place, don't move it, but still fire the object
17176  * access hook.
17177  */
17178  already_done = object_address_present(&thisobj, objsMoved);
17179  if (!already_done && oldNspOid != newNspOid)
17180  {
17181  /* check for duplicate name (more friendly than unique-index failure) */
17182  if (get_relname_relid(NameStr(classForm->relname),
17183  newNspOid) != InvalidOid)
17184  ereport(ERROR,
17185  (errcode(ERRCODE_DUPLICATE_TABLE),
17186  errmsg("relation \"%s\" already exists in schema \"%s\"",
17187  NameStr(classForm->relname),
17188  get_namespace_name(newNspOid))));
17189 
17190  /* classTup is a copy, so OK to scribble on */
17191  classForm->relnamespace = newNspOid;
17192 
17193  CatalogTupleUpdate(classRel, &classTup->t_self, classTup);
17194 
17195  /* Update dependency on schema if caller said so */
17196  if (hasDependEntry &&
17197  changeDependencyFor(RelationRelationId,
17198  relOid,
17199  NamespaceRelationId,
17200  oldNspOid,
17201  newNspOid) != 1)
17202  elog(ERROR, "could not change schema dependency for relation \"%s\"",
17203  NameStr(classForm->relname));
17204  }
17205  if (!already_done)
17206  {
17207  add_exact_object_address(&thisobj, objsMoved);
17208 
17209  InvokeObjectPostAlterHook(RelationRelationId, relOid, 0);
17210  }
17211 
17212  heap_freetuple(classTup);
17213 }
#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:458
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 17267 of file tablecmds.c.

17270 {
17271  Relation depRel;
17272  SysScanDesc scan;
17273  ScanKeyData key[2];
17274  HeapTuple tup;
17275 
17276  /*
17277  * SERIAL sequences are those having an auto dependency on one of the
17278  * table's columns (we don't care *which* column, exactly).
17279  */
17280  depRel = table_open(DependRelationId, AccessShareLock);
17281 
17282  ScanKeyInit(&key[0],
17283  Anum_pg_depend_refclassid,
17284  BTEqualStrategyNumber, F_OIDEQ,
17285  ObjectIdGetDatum(RelationRelationId));
17286  ScanKeyInit(&key[1],
17287  Anum_pg_depend_refobjid,
17288  BTEqualStrategyNumber, F_OIDEQ,
17290  /* we leave refobjsubid unspecified */
17291 
17292  scan = systable_beginscan(depRel, DependReferenceIndexId, true,
17293  NULL, 2, key);
17294 
17295  while (HeapTupleIsValid(tup = systable_getnext(scan)))
17296  {
17297  Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
17298  Relation seqRel;
17299 
17300  /* skip dependencies other than auto dependencies on columns */
17301  if (depForm->refobjsubid == 0 ||
17302  depForm->classid != RelationRelationId ||
17303  depForm->objsubid != 0 ||
17304  !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
17305  continue;
17306 
17307  /* Use relation_open just in case it's an index */
17308  seqRel = relation_open(depForm->objid, lockmode);
17309 
17310  /* skip non-sequence relations */
17311  if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
17312  {
17313  /* No need to keep the lock */
17314  relation_close(seqRel, lockmode);
17315  continue;
17316  }
17317 
17318  /* Fix the pg_class and pg_depend entries */
17319  AlterRelationNamespaceInternal(classRel, depForm->objid,
17320  oldNspOid, newNspOid,
17321  true, objsMoved);
17322 
17323  /*
17324  * Sequences used to have entries in pg_type, but no longer do. If we
17325  * ever re-instate that, we'll need to move the pg_type entry to the
17326  * new namespace, too (using AlterTypeNamespaceInternal).
17327  */
17328  Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
17329 
17330  /* Now we can close it. Keep the lock till end of transaction. */
17331  relation_close(seqRel, NoLock);
17332  }
17333 
17334  systable_endscan(scan);
17335 
17337 }
@ 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 4362 of file tablecmds.c.

4364 {
4365  Relation rel;
4366 
4367  /* Caller is required to provide an adequate lock. */
4368  rel = relation_open(context->relid, NoLock);
4369 
4370  CheckAlterTableIsSafe(rel);
4371 
4372  ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4373 }
#define stmt
Definition: indent_codes.h:59
tree context
Definition: radixtree.h:1835
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4715

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4436 of file tablecmds.c.

4437 {
4438  /*
4439  * This only works if we read catalog tables using MVCC snapshots.
4440  */
4441  ListCell *lcmd;
4443 
4444  foreach(lcmd, cmds)
4445  {
4446  AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4447  LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4448 
4449  switch (cmd->subtype)
4450  {
4451  /*
4452  * These subcommands rewrite the heap, so require full locks.
4453  */
4454  case AT_AddColumn: /* may rewrite heap, in some cases and visible
4455  * to SELECT */
4456  case AT_SetAccessMethod: /* must rewrite heap */
4457  case AT_SetTableSpace: /* must rewrite heap */
4458  case AT_AlterColumnType: /* must rewrite heap */
4459  cmd_lockmode = AccessExclusiveLock;
4460  break;
4461 
4462  /*
4463  * These subcommands may require addition of toast tables. If
4464  * we add a toast table to a table currently being scanned, we
4465  * might miss data added to the new toast table by concurrent
4466  * insert transactions.
4467  */
4468  case AT_SetStorage: /* may add toast tables, see
4469  * ATRewriteCatalogs() */
4470  cmd_lockmode = AccessExclusiveLock;
4471  break;
4472 
4473  /*
4474  * Removing constraints can affect SELECTs that have been
4475  * optimized assuming the constraint holds true. See also
4476  * CloneFkReferenced.
4477  */
4478  case AT_DropConstraint: /* as DROP INDEX */
4479  case AT_DropNotNull: /* may change some SQL plans */
4480  cmd_lockmode = AccessExclusiveLock;
4481  break;
4482 
4483  /*
4484  * Subcommands that may be visible to concurrent SELECTs
4485  */
4486  case AT_DropColumn: /* change visible to SELECT */
4487  case AT_AddColumnToView: /* CREATE VIEW */
4488  case AT_DropOids: /* used to equiv to DropColumn */
4489  case AT_EnableAlwaysRule: /* may change SELECT rules */
4490  case AT_EnableReplicaRule: /* may change SELECT rules */
4491  case AT_EnableRule: /* may change SELECT rules */
4492  case AT_DisableRule: /* may change SELECT rules */
4493  cmd_lockmode = AccessExclusiveLock;
4494  break;
4495 
4496  /*
4497  * Changing owner may remove implicit SELECT privileges
4498  */
4499  case AT_ChangeOwner: /* change visible to SELECT */
4500  cmd_lockmode = AccessExclusiveLock;
4501  break;
4502 
4503  /*
4504  * Changing foreign table options may affect optimization.
4505  */
4506  case AT_GenericOptions:
4508  cmd_lockmode = AccessExclusiveLock;
4509  break;
4510 
4511  /*
4512  * These subcommands affect write operations only.
4513  */
4514  case AT_EnableTrig:
4515  case AT_EnableAlwaysTrig:
4516  case AT_EnableReplicaTrig:
4517  case AT_EnableTrigAll:
4518  case AT_EnableTrigUser:
4519  case AT_DisableTrig:
4520  case AT_DisableTrigAll:
4521  case AT_DisableTrigUser:
4522  cmd_lockmode = ShareRowExclusiveLock;
4523  break;
4524 
4525  /*
4526  * These subcommands affect write operations only. XXX
4527  * Theoretically, these could be ShareRowExclusiveLock.
4528  */
4529  case AT_ColumnDefault:
4531  case AT_AlterConstraint:
4532  case AT_AddIndex: /* from ADD CONSTRAINT */
4533  case AT_AddIndexConstraint:
4534  case AT_ReplicaIdentity:
4535  case AT_SetNotNull:
4536  case AT_EnableRowSecurity:
4537  case AT_DisableRowSecurity:
4538  case AT_ForceRowSecurity:
4539  case AT_NoForceRowSecurity:
4540  case AT_AddIdentity:
4541  case AT_DropIdentity:
4542  case AT_SetIdentity:
4543  case AT_SetExpression:
4544  case AT_DropExpression:
4545  case AT_SetCompression:
4546  cmd_lockmode = AccessExclusiveLock;
4547  break;
4548 
4549  case AT_AddConstraint:
4550  case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4551  case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4552  if (IsA(cmd->def, Constraint))
4553  {
4554  Constraint *con = (Constraint *) cmd->def;
4555 
4556  switch (con->contype)
4557  {
4558  case CONSTR_EXCLUSION:
4559  case CONSTR_PRIMARY:
4560  case CONSTR_UNIQUE:
4561 
4562  /*
4563  * Cases essentially the same as CREATE INDEX. We
4564  * could reduce the lock strength to ShareLock if
4565  * we can work out how to allow concurrent catalog
4566  * updates. XXX Might be set down to
4567  * ShareRowExclusiveLock but requires further
4568  * analysis.
4569  */
4570  cmd_lockmode = AccessExclusiveLock;
4571  break;
4572  case CONSTR_FOREIGN:
4573 
4574  /*
4575  * We add triggers to both tables when we add a
4576  * Foreign Key, so the lock level must be at least
4577  * as strong as CREATE TRIGGER.
4578  */
4579  cmd_lockmode = ShareRowExclusiveLock;
4580  break;
4581 
4582  default:
4583  cmd_lockmode = AccessExclusiveLock;
4584  }
4585  }
4586  break;
4587 
4588  /*
4589  * These subcommands affect inheritance behaviour. Queries
4590  * started before us will continue to see the old inheritance
4591  * behaviour, while queries started after we commit will see
4592  * new behaviour. No need to prevent reads or writes to the
4593  * subtable while we hook it up though. Changing the TupDesc
4594  * may be a problem, so keep highest lock.
4595  */
4596  case AT_AddInherit:
4597  case AT_DropInherit:
4598  cmd_lockmode = AccessExclusiveLock;
4599  break;
4600 
4601  /*
4602  * These subcommands affect implicit row type conversion. They
4603  * have affects similar to CREATE/DROP CAST on queries. don't
4604  * provide for invalidating parse trees as a result of such
4605  * changes, so we keep these at AccessExclusiveLock.
4606  */
4607  case AT_AddOf:
4608  case AT_DropOf:
4609  cmd_lockmode = AccessExclusiveLock;
4610  break;
4611 
4612  /*
4613  * Only used by CREATE OR REPLACE VIEW which must conflict
4614  * with an SELECTs currently using the view.
4615  */
4616  case AT_ReplaceRelOptions:
4617  cmd_lockmode = AccessExclusiveLock;
4618  break;
4619 
4620  /*
4621  * These subcommands affect general strategies for performance
4622  * and maintenance, though don't change the semantic results
4623  * from normal data reads and writes. Delaying an ALTER TABLE
4624  * behind currently active writes only delays the point where
4625  * the new strategy begins to take effect, so there is no
4626  * benefit in waiting. In this case the minimum restriction
4627  * applies: we don't currently allow concurrent catalog
4628  * updates.
4629  */
4630  case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4631  case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4632  case AT_DropCluster: /* Uses MVCC in getIndexes() */
4633  case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4634  case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4635  cmd_lockmode = ShareUpdateExclusiveLock;
4636  break;
4637 
4638  case AT_SetLogged:
4639  case AT_SetUnLogged:
4640  cmd_lockmode = AccessExclusiveLock;
4641  break;
4642 
4643  case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4644  cmd_lockmode = ShareUpdateExclusiveLock;
4645  break;
4646 
4647  /*
4648  * Rel options are more complex than first appears. Options
4649  * are set here for tables, views and indexes; for historical
4650  * reasons these can all be used with ALTER TABLE, so we can't
4651  * decide between them using the basic grammar.
4652  */
4653  case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4654  * getTables() */
4655  case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4656  * getTables() */
4657  cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
4658  break;
4659 
4660  case AT_AttachPartition:
4661  cmd_lockmode = ShareUpdateExclusiveLock;
4662  break;
4663 
4664  case AT_DetachPartition:
4665  if (((PartitionCmd *) cmd->def)->concurrent)
4666  cmd_lockmode = ShareUpdateExclusiveLock;
4667  else
4668  cmd_lockmode = AccessExclusiveLock;
4669  break;
4670 
4672  cmd_lockmode = ShareUpdateExclusiveLock;
4673  break;
4674 
4675  case AT_SplitPartition:
4676  cmd_lockmode = AccessExclusiveLock;
4677  break;
4678 
4679  case AT_MergePartitions:
4680  cmd_lockmode = AccessExclusiveLock;
4681  break;
4682 
4683  case AT_CheckNotNull:
4684 
4685  /*
4686  * This only examines the table's schema; but lock must be
4687  * strong enough to prevent concurrent DROP NOT NULL.
4688  */
4689  cmd_lockmode = AccessShareLock;
4690  break;
4691 
4692  default: /* oops */
4693  elog(ERROR, "unrecognized alter table type: %d",
4694  (int) cmd->subtype);
4695  break;
4696  }
4697 
4698  /*
4699  * Take the greatest lockmode from any subcommand
4700  */
4701  if (cmd_lockmode > lockmode)
4702  lockmode = cmd_lockmode;
4703  }
4704 
4705  return lockmode;
4706 }
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:2712
@ CONSTR_EXCLUSION
Definition: parsenodes.h:2713
@ CONSTR_PRIMARY
Definition: parsenodes.h:2711
#define lfirst(lc)
Definition: pg_list.h:172
LOCKMODE AlterTableGetRelOptionsLockLevel(List *defList)
Definition: reloptions.c:2108
AlterTableType subtype
Definition: parsenodes.h:2434
ConstrType contype
Definition: parsenodes.h:2736

References AccessExclusiveLock, AccessShareLock, 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_CheckNotNull, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_MergePartitions, AT_NoForceRowSecurity, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, AT_SplitPartition, AT_ValidateConstraint, CONSTR_EXCLUSION, CONSTR_FOREIGN, CONSTR_PRIMARY, CONSTR_UNIQUE, Constraint::contype, AlterTableCmd::def, elog, ERROR, IsA, lfirst, ShareRowExclusiveLock, ShareUpdateExclusiveLock, and AlterTableCmd::subtype.

Referenced by AlterTableInternal(), and ProcessUtilitySlow().

◆ AlterTableInternal()

void AlterTableInternal ( Oid  relid,
List cmds,
bool  recurse 
)

Definition at line 4391 of file tablecmds.c.

4392 {
4393  Relation rel;
4394  LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4395 
4396  rel = relation_open(relid, lockmode);
4397 
4399 
4400  ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4401 }
void EventTriggerAlterTableRelid(Oid objectId)
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition: tablecmds.c:4436

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4303 of file tablecmds.c.

4304 {
4305  return RangeVarGetRelidExtended(stmt->relation, lockmode,
4306  stmt->missing_ok ? RVR_MISSING_OK : 0,
4308  (void *) stmt);
4309 }
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:17677

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

Referenced by ProcessUtilitySlow().

◆ AlterTableMoveAll()

Oid AlterTableMoveAll ( AlterTableMoveAllStmt stmt)

Definition at line 15222 of file tablecmds.c.

15223 {
15224  List *relations = NIL;
15225  ListCell *l;
15226  ScanKeyData key[1];
15227  Relation rel;
15228  TableScanDesc scan;
15229  HeapTuple tuple;
15230  Oid orig_tablespaceoid;
15231  Oid new_tablespaceoid;
15232  List *role_oids = roleSpecsToIds(stmt->roles);
15233 
15234  /* Ensure we were not asked to move something we can't */
15235  if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
15236  stmt->objtype != OBJECT_MATVIEW)
15237  ereport(ERROR,
15238  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15239  errmsg("only tables, indexes, and materialized views exist in tablespaces")));
15240 
15241  /* Get the orig and new tablespace OIDs */
15242  orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
15243  new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
15244 
15245  /* Can't move shared relations in to or out of pg_global */
15246  /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
15247  if (orig_tablespaceoid == GLOBALTABLESPACE_OID ||
15248  new_tablespaceoid == GLOBALTABLESPACE_OID)
15249  ereport(ERROR,
15250  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
15251  errmsg("cannot move relations in to or out of pg_global tablespace")));
15252 
15253  /*
15254  * Must have CREATE rights on the new tablespace, unless it is the
15255  * database default tablespace (which all users implicitly have CREATE
15256  * rights on).
15257  */
15258  if (OidIsValid(new_tablespaceoid) && new_tablespaceoid != MyDatabaseTableSpace)
15259  {
15260  AclResult aclresult;
15261 
15262  aclresult = object_aclcheck(TableSpaceRelationId, new_tablespaceoid, GetUserId(),
15263  ACL_CREATE);
15264  if (aclresult != ACLCHECK_OK)
15265  aclcheck_error(aclresult, OBJECT_TABLESPACE,
15266  get_tablespace_name(new_tablespaceoid));
15267  }
15268 
15269  /*
15270  * Now that the checks are done, check if we should set either to
15271  * InvalidOid because it is our database's default tablespace.
15272  */
15273  if (orig_tablespaceoid == MyDatabaseTableSpace)
15274  orig_tablespaceoid = InvalidOid;
15275 
15276  if (new_tablespaceoid == MyDatabaseTableSpace)
15277  new_tablespaceoid = InvalidOid;
15278 
15279  /* no-op */
15280  if (orig_tablespaceoid == new_tablespaceoid)
15281  return new_tablespaceoid;
15282 
15283  /*
15284  * Walk the list of objects in the tablespace and move them. This will
15285  * only find objects in our database, of course.
15286  */
15287  ScanKeyInit(&key[0],
15288  Anum_pg_class_reltablespace,
15289  BTEqualStrategyNumber, F_OIDEQ,
15290  ObjectIdGetDatum(orig_tablespaceoid));
15291 
15292  rel = table_open(RelationRelationId, AccessShareLock);
15293  scan = table_beginscan_catalog(rel, 1, key);
15294  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
15295  {
15296  Form_pg_class relForm = (Form_pg_class) GETSTRUCT(tuple);
15297  Oid relOid = relForm->oid;
15298 
15299  /*
15300  * Do not move objects in pg_catalog as part of this, if an admin
15301  * really wishes to do so, they can issue the individual ALTER
15302  * commands directly.
15303  *
15304  * Also, explicitly avoid any shared tables, temp tables, or TOAST
15305  * (TOAST will be moved with the main table).
15306  */
15307  if (IsCatalogNamespace(relForm->relnamespace) ||
15308  relForm->relisshared ||
15309  isAnyTempNamespace(relForm->relnamespace) ||
15310  IsToastNamespace(relForm->relnamespace))
15311  continue;
15312 
15313  /* Only move the object type requested */
15314  if ((stmt->objtype == OBJECT_TABLE &&
15315  relForm->relkind != RELKIND_RELATION &&
15316  relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
15317  (stmt->objtype == OBJECT_INDEX &&
15318  relForm->relkind != RELKIND_INDEX &&
15319  relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
15320  (stmt->objtype == OBJECT_MATVIEW &&
15321  relForm->relkind != RELKIND_MATVIEW))
15322  continue;
15323 
15324  /* Check if we are only moving objects owned by certain roles */
15325  if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
15326  continue;
15327 
15328  /*
15329  * Handle permissions-checking here since we are locking the tables
15330  * and also to avoid doing a bunch of work only to fail part-way. Note
15331  * that permissions will also be checked by AlterTableInternal().
15332  *
15333  * Caller must be considered an owner on the table to move it.
15334  */
15335  if (!object_ownercheck(RelationRelationId, relOid, GetUserId()))
15337  NameStr(relForm->relname));
15338 
15339  if (stmt->nowait &&
15341  ereport(ERROR,
15342  (errcode(ERRCODE_OBJECT_IN_USE),
15343  errmsg("aborting because lock on relation \"%s.%s\" is not available",
15344  get_namespace_name(relForm->relnamespace),
15345  NameStr(relForm->relname))));
15346  else
15348 
15349  /* Add to our list of objects to move */
15350  relations = lappend_oid(relations, relOid);
15351  }
15352 
15353  table_endscan(scan);
15355 
15356  if (relations == NIL)
15357  ereport(NOTICE,
15358  (errcode(ERRCODE_NO_DATA_FOUND),
15359  errmsg("no matching relations in tablespace \"%s\" found",
15360  orig_tablespaceoid == InvalidOid ? "(database default)" :
15361  get_tablespace_name(orig_tablespaceoid))));
15362 
15363  /* Everything is locked, loop through and move all of the relations. */
15364  foreach(l, relations)
15365  {
15366  List *cmds = NIL;
15368 
15369  cmd->subtype = AT_SetTableSpace;
15370  cmd->name = stmt->new_tablespacename;
15371 
15372  cmds = lappend(cmds, cmd);
15373 
15375  /* OID is set by AlterTableInternal */
15376  AlterTableInternal(lfirst_oid(l), cmds, false);
15378  }
15379 
15380  return new_tablespaceoid;
15381 }
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:2700
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition: aclchk.c:3888
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition: aclchk.c:4142
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:221
bool IsCatalogNamespace(Oid namespaceId)
Definition: catalog.c:203
#define NOTICE
Definition: elog.h:35
void EventTriggerAlterTableStart(Node *parsetree)
void EventTriggerAlterTableEnd(void)
Oid MyDatabaseTableSpace
Definition: globals.c:94
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition: heapam.c:1252
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:2284
@ OBJECT_TABLESPACE
Definition: parsenodes.h:2303
@ OBJECT_INDEX
Definition: parsenodes.h:2281
@ OBJECT_TABLE
Definition: parsenodes.h:2302
#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:1019
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition: tablecmds.c:4391

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

17045 {
17046  Relation rel;
17047  Oid relid;
17048  Oid oldNspOid;
17049  Oid nspOid;
17050  RangeVar *newrv;
17051  ObjectAddresses *objsMoved;
17052  ObjectAddress myself;
17053 
17055  stmt->missing_ok ? RVR_MISSING_OK : 0,
17057  (void *) stmt);
17058 
17059  if (!OidIsValid(relid))
17060  {
17061  ereport(NOTICE,
17062  (errmsg("relation \"%s\" does not exist, skipping",
17063  stmt->relation->relname)));
17064  return InvalidObjectAddress;
17065  }
17066 
17067  rel = relation_open(relid, NoLock);
17068 
17069  oldNspOid = RelationGetNamespace(rel);
17070 
17071  /* If it's an owned sequence, disallow moving it by itself. */
17072  if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
17073  {
17074  Oid tableId;
17075  int32 colId;
17076 
17077  if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
17078  sequenceIsOwned(relid, DEPENDENCY_INTERNAL, &tableId, &colId))
17079  ereport(ERROR,
17080  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
17081  errmsg("cannot move an owned sequence into another schema"),
17082  errdetail("Sequence \"%s\" is linked to table \"%s\".",
17084  get_rel_name(tableId))));
17085  }
17086 
17087  /* Get and lock schema OID and check its permissions. */
17088  newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
17089  nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL);
17090 
17091  /* common checks on switching namespaces */
17092  CheckSetNamespace(oldNspOid, nspOid);
17093 
17094  objsMoved = new_object_addresses();
17095  AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved);
17096  free_object_addresses(objsMoved);
17097 
17098  ObjectAddressSet(myself, RelationRelationId, relid);
17099 
17100  if (oldschema)
17101  *oldschema = oldNspOid;
17102 
17103  /* close rel, but keep lock until commit */
17104  relation_close(rel, NoLock);
17105 
17106  return myself;
17107 }
signed int int32
Definition: c.h:494
ObjectAddresses * new_object_addresses(void)
Definition: dependency.c:2487
void free_object_addresses(ObjectAddresses *addrs)
Definition: dependency.c:2773
int errdetail(const char *fmt,...)
Definition: elog.c:1203
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:829
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:17115

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

17117 {
17118  Relation classRel;
17119 
17120  Assert(objsMoved != NULL);
17121 
17122  /* OK, modify the pg_class row and pg_depend entry */
17123  classRel = table_open(RelationRelationId, RowExclusiveLock);
17124 
17125  AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
17126  nspOid, true, objsMoved);
17127 
17128  /* Fix the table's row type too, if it has one */
17129  if (OidIsValid(rel->rd_rel->reltype))
17130  AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid,
17131  false, /* isImplicitArray */
17132  false, /* ignoreDependent */
17133  false, /* errorOnTableType */
17134  objsMoved);
17135 
17136  /* Fix other dependent stuff */
17137  AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
17138  AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid,
17139  objsMoved, AccessExclusiveLock);
17140  AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid,
17141  false, objsMoved);
17142 
17143  table_close(classRel, RowExclusiveLock);
17144 }
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:17267
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Definition: tablecmds.c:17222
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition: typecmds.c:4156

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

◆ ATAddCheckConstraint()

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

Definition at line 9445 of file tablecmds.c.

9448 {
9449  List *newcons;
9450  ListCell *lcon;
9451  List *children;
9452  ListCell *child;
9454 
9455  /* At top level, permission check was done in ATPrepCmd, else do it */
9456  if (recursing)
9458 
9459  /*
9460  * Call AddRelationNewConstraints to do the work, making sure it works on
9461  * a copy of the Constraint so transformExpr can't modify the original. It
9462  * returns a list of cooked constraints.
9463  *
9464  * If the constraint ends up getting merged with a pre-existing one, it's
9465  * omitted from the returned list, which is what we want: we do not need
9466  * to do any validation work. That can only happen at child tables,
9467  * though, since we disallow merging at the top level.
9468  */
9469  newcons = AddRelationNewConstraints(rel, NIL,
9470  list_make1(copyObject(constr)),
9471  recursing || is_readd, /* allow_merge */
9472  !recursing, /* is_local */
9473  is_readd, /* is_internal */
9474  NULL); /* queryString not available
9475  * here */
9476 
9477  /* we don't expect more than one constraint here */
9478  Assert(list_length(newcons) <= 1);
9479 
9480  /* Add each to-be-validated constraint to Phase 3's queue */
9481  foreach(lcon, newcons)
9482  {
9483  CookedConstraint *ccon = (CookedConstraint *) lfirst(lcon);
9484 
9485  if (!ccon->skip_validation)
9486  {
9487  NewConstraint *newcon;
9488 
9489  newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
9490  newcon->name = ccon->name;
9491  newcon->contype = ccon->contype;
9492  newcon->qual = ccon->expr;
9493 
9494  tab->constraints = lappend(tab->constraints, newcon);
9495  }
9496 
9497  /* Save the actually assigned name if it was defaulted */
9498  if (constr->conname == NULL)
9499  constr->conname = ccon->name;
9500 
9501  ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
9502  }
9503 
9504  /* At this point we must have a locked-down name to use */
9505  Assert(constr->conname != NULL);
9506 
9507  /* Advance command counter in case same table is visited multiple times */
9509 
9510  /*
9511  * If the constraint got merged with an existing constraint, we're done.
9512  * We mustn't recurse to child tables in this case, because they've
9513  * already got the constraint, and visiting them again would lead to an
9514  * incorrect value for coninhcount.
9515  */
9516  if (newcons == NIL)
9517  return address;
9518 
9519  /*
9520  * If adding a NO INHERIT constraint, no need to find our children.
9521  */
9522  if (constr->is_no_inherit)
9523  return address;
9524 
9525  /*
9526  * Propagate to children as appropriate. Unlike most other ALTER
9527  * routines, we have to do this one level of recursion at a time; we can't
9528  * use find_all_inheritors to do it in one pass.
9529  */
9530  children =
9532 
9533  /*
9534  * Check if ONLY was specified with ALTER TABLE. If so, allow the
9535  * constraint creation only if there are no children currently. Error out
9536  * otherwise.
9537  */
9538  if (!recurse && children != NIL)
9539  ereport(ERROR,
9540  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9541  errmsg("constraint must be added to child tables too")));
9542 
9543  foreach(child, children)
9544  {
9545  Oid childrelid = lfirst_oid(child);
9546  Relation childrel;
9547  AlteredTableInfo *childtab;
9548 
9549  /* find_inheritance_children already got lock */
9550  childrel = table_open(childrelid, NoLock);
9551  CheckAlterTableIsSafe(childrel);
9552 
9553  /* Find or create work queue entry for this table */
9554  childtab = ATGetQueueEntry(wqueue, childrel);
9555 
9556  /* Recurse to child */
9557  ATAddCheckConstraint(wqueue, childtab, childrel,
9558  constr, recurse, true, is_readd, lockmode);
9559 
9560  table_close(childrel, NoLock);
9561  }
9562 
9563  return address;
9564 }
List * AddRelationNewConstraints(Relation rel, List *newColDefaults, List *newConstraints, bool allow_merge, bool is_local, bool is_internal, const char *queryString)
Definition: heap.c:2258
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
bool is_no_inherit
Definition: parsenodes.h:2742
Oid conoid
Definition: heap.h:38
char * name
Definition: heap.h:39
bool skip_validation
Definition: heap.h:42
ConstrType contype
Definition: heap.h:37
Node * expr
Definition: heap.h:41
#define ATT_TABLE
Definition: tablecmds.c:325
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition: tablecmds.c:6544
#define ATT_FOREIGN_TABLE
Definition: tablecmds.c:330
static ObjectAddress ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9445

References AddRelationNewConstraints(), Assert, AT_AddConstraint, ATGetQueueEntry(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_TABLE, CheckAlterTableIsSafe(), CommandCounterIncrement(), Constraint::conname, CookedConstraint::conoid, AlteredTableInfo::constraints, NewConstraint::contype, CookedConstraint::contype, copyObject, ereport, errcode(), errmsg(), ERROR, CookedConstraint::expr, find_inheritance_children(), InvalidObjectAddress, Constraint::is_no_inherit, lappend(), lfirst, lfirst_oid, list_length(), list_make1, NewConstraint::name, CookedConstraint::name, NIL, NoLock, ObjectAddressSet, palloc0(), NewConstraint::qual, RelationGetRelid, 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 9582 of file tablecmds.c.

9585 {
9586  Relation pkrel;
9587  int16 pkattnum[INDEX_MAX_KEYS] = {0};
9588  int16 fkattnum[INDEX_MAX_KEYS] = {0};
9589  Oid pktypoid[INDEX_MAX_KEYS] = {0};
9590  Oid fktypoid[INDEX_MAX_KEYS] = {0};
9591  Oid opclasses[INDEX_MAX_KEYS] = {0};
9592  Oid pfeqoperators[INDEX_MAX_KEYS] = {0};
9593  Oid ppeqoperators[INDEX_MAX_KEYS] = {0};
9594  Oid ffeqoperators[INDEX_MAX_KEYS] = {0};
9595  int16 fkdelsetcols[INDEX_MAX_KEYS] = {0};
9596  int i;
9597  int numfks,
9598  numpks,
9599  numfkdelsetcols;
9600  Oid indexOid;
9601  bool old_check_ok;
9602  ObjectAddress address;
9603  ListCell *old_pfeqop_item = list_head(fkconstraint->old_conpfeqop);
9604 
9605  /*
9606  * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
9607  * delete rows out from under us.
9608  */
9609  if (OidIsValid(fkconstraint->old_pktable_oid))
9610  pkrel = table_open(fkconstraint->old_pktable_oid, ShareRowExclusiveLock);
9611  else
9612  pkrel = table_openrv(fkconstraint->pktable, ShareRowExclusiveLock);
9613 
9614  /*
9615  * Validity checks (permission checks wait till we have the column
9616  * numbers)
9617  */
9618  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9619  {
9620  if (!recurse)
9621  ereport(ERROR,
9622  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9623  errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9625  RelationGetRelationName(pkrel))));
9626  if (fkconstraint->skip_validation && !fkconstraint->initially_valid)
9627  ereport(ERROR,
9628  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9629  errmsg("cannot add NOT VALID foreign key on partitioned table \"%s\" referencing relation \"%s\"",
9631  RelationGetRelationName(pkrel)),
9632  errdetail("This feature is not yet supported on partitioned tables.")));
9633  }
9634 
9635  if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
9636  pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
9637  ereport(ERROR,
9638  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
9639  errmsg("referenced relation \"%s\" is not a table",
9640  RelationGetRelationName(pkrel))));
9641 
9642  if (!allowSystemTableMods && IsSystemRelation(pkrel))
9643  ereport(ERROR,
9644  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
9645  errmsg("permission denied: \"%s\" is a system catalog",
9646  RelationGetRelationName(pkrel))));
9647 
9648  /*
9649  * References from permanent or unlogged tables to temp tables, and from
9650  * permanent tables to unlogged tables, are disallowed because the
9651  * referenced data can vanish out from under us. References from temp
9652  * tables to any other table type are also disallowed, because other
9653  * backends might need to run the RI triggers on the perm table, but they
9654  * can't reliably see tuples in the local buffers of other backends.
9655  */
9656  switch (rel->rd_rel->relpersistence)
9657  {
9658  case RELPERSISTENCE_PERMANENT:
9659  if (!RelationIsPermanent(pkrel))
9660  ereport(ERROR,
9661  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9662  errmsg("constraints on permanent tables may reference only permanent tables")));
9663  break;
9664  case RELPERSISTENCE_UNLOGGED:
9665  if (!RelationIsPermanent(pkrel)
9666  && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
9667  ereport(ERROR,
9668  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9669  errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
9670  break;
9671  case RELPERSISTENCE_TEMP:
9672  if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
9673  ereport(ERROR,
9674  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9675  errmsg("constraints on temporary tables may reference only temporary tables")));
9676  if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
9677  ereport(ERROR,
9678  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9679  errmsg("constraints on temporary tables must involve temporary tables of this session")));
9680  break;
9681  }
9682 
9683  /*
9684  * Look up the referencing attributes to make sure they exist, and record
9685  * their attnums and type OIDs.
9686  */
9688  fkconstraint->fk_attrs,
9689  fkattnum, fktypoid);
9690 
9691  numfkdelsetcols = transformColumnNameList(RelationGetRelid(rel),
9692  fkconstraint->fk_del_set_cols,
9693  fkdelsetcols, NULL);
9694  validateFkOnDeleteSetColumns(numfks, fkattnum,
9695  numfkdelsetcols, fkdelsetcols,
9696  fkconstraint->fk_del_set_cols);
9697 
9698  /*
9699  * If the attribute list for the referenced table was omitted, lookup the
9700  * definition of the primary key and use it. Otherwise, validate the
9701  * supplied attribute list. In either case, discover the index OID and
9702  * index opclasses, and the attnums and type OIDs of the attributes.
9703  */
9704  if (fkconstraint->pk_attrs == NIL)
9705  {
9706  numpks = transformFkeyGetPrimaryKey(pkrel, &indexOid,
9707  &fkconstraint->pk_attrs,
9708  pkattnum, pktypoid,
9709  opclasses);
9710  }
9711  else
9712  {
9713  numpks = transformColumnNameList(RelationGetRelid(pkrel),
9714  fkconstraint->pk_attrs,
9715  pkattnum, pktypoid);
9716  /* Look for an index matching the column list */
9717  indexOid = transformFkeyCheckAttrs(pkrel, numpks, pkattnum,
9718  opclasses);
9719  }
9720 
9721  /*
9722  * Now we can check permissions.
9723  */
9724  checkFkeyPermissions(pkrel, pkattnum, numpks);
9725 
9726  /*
9727  * Check some things for generated columns.
9728  */
9729  for (i = 0; i < numfks; i++)
9730  {
9731  char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
9732 
9733  if (attgenerated)
9734  {
9735  /*
9736  * Check restrictions on UPDATE/DELETE actions, per SQL standard
9737  */
9738  if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
9739  fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
9740  fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
9741  ereport(ERROR,
9742  (errcode(ERRCODE_SYNTAX_ERROR),
9743  errmsg("invalid %s action for foreign key constraint containing generated column",
9744  "ON UPDATE")));
9745  if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
9746  fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
9747  ereport(ERROR,
9748  (errcode(ERRCODE_SYNTAX_ERROR),
9749  errmsg("invalid %s action for foreign key constraint containing generated column",
9750  "ON DELETE")));
9751  }
9752  }
9753 
9754  /*
9755  * Look up the equality operators to use in the constraint.
9756  *
9757  * Note that we have to be careful about the difference between the actual
9758  * PK column type and the opclass' declared input type, which might be
9759  * only binary-compatible with it. The declared opcintype is the right
9760  * thing to probe pg_amop with.
9761  */
9762  if (numfks != numpks)
9763  ereport(ERROR,
9764  (errcode(ERRCODE_INVALID_FOREIGN_KEY),
9765  errmsg("number of referencing and referenced columns for foreign key disagree")));
9766 
9767  /*
9768  * On the strength of a previous constraint, we might avoid scanning
9769  * tables to validate this one. See below.
9770  */
9771  old_check_ok = (fkconstraint->old_conpfeqop != NIL);
9772  Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
9773 
9774  for (i = 0; i < numpks; i++)
9775  {
9776  Oid pktype = pktypoid[i];
9777  Oid fktype = fktypoid[i];
9778  Oid fktyped;
9779  HeapTuple cla_ht;
9780  Form_pg_opclass cla_tup;
9781  Oid amid;
9782  Oid opfamily;
9783  Oid opcintype;
9784  Oid pfeqop;
9785  Oid ppeqop;
9786  Oid ffeqop;
9787  int16 eqstrategy;
9788  Oid pfeqop_right;
9789 
9790  /* We need several fields out of the pg_opclass entry */
9791  cla_ht = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclasses[i]));
9792  if (!HeapTupleIsValid(cla_ht))
9793  elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
9794  cla_tup = (Form_pg_opclass) GETSTRUCT(cla_ht);
9795  amid = cla_tup->opcmethod;
9796  opfamily = cla_tup->opcfamily;
9797  opcintype = cla_tup->opcintype;
9798  ReleaseSysCache(cla_ht);
9799 
9800  /*
9801  * Check it's a btree; currently this can never fail since no other
9802  * index AMs support unique indexes. If we ever did have other types
9803  * of unique indexes, we'd need a way to determine which operator
9804  * strategy number is equality. (Is it reasonable to insist that
9805  * every such index AM use btree's number for equality?)
9806  */
9807  if (amid != BTREE_AM_OID)
9808  elog(ERROR, "only b-tree indexes are supported for foreign keys");
9809  eqstrategy = BTEqualStrategyNumber;
9810 
9811  /*
9812  * There had better be a primary equality operator for the index.
9813  * We'll use it for PK = PK comparisons.
9814  */
9815  ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
9816  eqstrategy);
9817 
9818  if (!OidIsValid(ppeqop))
9819  elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
9820  eqstrategy, opcintype, opcintype, opfamily);
9821 
9822  /*
9823  * Are there equality operators that take exactly the FK type? Assume
9824  * we should look through any domain here.
9825  */
9826  fktyped = getBaseType(fktype);
9827 
9828  pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
9829  eqstrategy);
9830  if (OidIsValid(pfeqop))
9831  {
9832  pfeqop_right = fktyped;
9833  ffeqop = get_opfamily_member(opfamily, fktyped, fktyped,
9834  eqstrategy);
9835  }
9836  else
9837  {
9838  /* keep compiler quiet */
9839  pfeqop_right = InvalidOid;
9840  ffeqop = InvalidOid;
9841  }
9842 
9843  if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9844  {
9845  /*
9846  * Otherwise, look for an implicit cast from the FK type to the
9847  * opcintype, and if found, use the primary equality operator.
9848  * This is a bit tricky because opcintype might be a polymorphic
9849  * type such as ANYARRAY or ANYENUM; so what we have to test is
9850  * whether the two actual column types can be concurrently cast to
9851  * that type. (Otherwise, we'd fail to reject combinations such
9852  * as int[] and point[].)
9853  */
9854  Oid input_typeids[2];
9855  Oid target_typeids[2];
9856 
9857  input_typeids[0] = pktype;
9858  input_typeids[1] = fktype;
9859  target_typeids[0] = opcintype;
9860  target_typeids[1] = opcintype;
9861  if (can_coerce_type(2, input_typeids, target_typeids,
9863  {
9864  pfeqop = ffeqop = ppeqop;
9865  pfeqop_right = opcintype;
9866  }
9867  }
9868 
9869  if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
9870  ereport(ERROR,
9871  (errcode(ERRCODE_DATATYPE_MISMATCH),
9872  errmsg("foreign key constraint \"%s\" cannot be implemented",
9873  fkconstraint->conname),
9874  errdetail("Key columns \"%s\" and \"%s\" "
9875  "are of incompatible types: %s and %s.",
9876  strVal(list_nth(fkconstraint->fk_attrs, i)),
9877  strVal(list_nth(fkconstraint->pk_attrs, i)),
9878  format_type_be(fktype),
9879  format_type_be(pktype))));
9880 
9881  if (old_check_ok)
9882  {
9883  /*
9884  * When a pfeqop changes, revalidate the constraint. We could
9885  * permit intra-opfamily changes, but that adds subtle complexity
9886  * without any concrete benefit for core types. We need not
9887  * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
9888  */
9889  old_check_ok = (pfeqop == lfirst_oid(old_pfeqop_item));
9890  old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
9891  old_pfeqop_item);
9892  }
9893  if (old_check_ok)
9894  {
9895  Oid old_fktype;
9896  Oid new_fktype;
9897  CoercionPathType old_pathtype;
9898  CoercionPathType new_pathtype;
9899  Oid old_castfunc;
9900  Oid new_castfunc;
9902  fkattnum[i] - 1);
9903 
9904  /*
9905  * Identify coercion pathways from each of the old and new FK-side
9906  * column types to the right (foreign) operand type of the pfeqop.
9907  * We may assume that pg_constraint.conkey is not changing.
9908  */
9909  old_fktype = attr->atttypid;
9910  new_fktype = fktype;
9911  old_pathtype = findFkeyCast(pfeqop_right, old_fktype,
9912  &old_castfunc);
9913  new_pathtype = findFkeyCast(pfeqop_right, new_fktype,
9914  &new_castfunc);
9915 
9916  /*
9917  * Upon a change to the cast from the FK column to its pfeqop
9918  * operand, revalidate the constraint. For this evaluation, a
9919  * binary coercion cast is equivalent to no cast at all. While
9920  * type implementors should design implicit casts with an eye
9921  * toward consistency of operations like equality, we cannot
9922  * assume here that they have done so.
9923  *
9924  * A function with a polymorphic argument could change behavior
9925  * arbitrarily in response to get_fn_expr_argtype(). Therefore,
9926  * when the cast destination is polymorphic, we only avoid
9927  * revalidation if the input type has not changed at all. Given
9928  * just the core data types and operator classes, this requirement
9929  * prevents no would-be optimizations.
9930  *
9931  * If the cast converts from a base type to a domain thereon, then
9932  * that domain type must be the opcintype of the unique index.
9933  * Necessarily, the primary key column must then be of the domain
9934  * type. Since the constraint was previously valid, all values on
9935  * the foreign side necessarily exist on the primary side and in
9936  * turn conform to the domain. Consequently, we need not treat
9937  * domains specially here.
9938  *
9939  * Since we require that all collations share the same notion of
9940  * equality (which they do, because texteq reduces to bitwise
9941  * equality), we don't compare collation here.
9942  *
9943  * We need not directly consider the PK type. It's necessarily
9944  * binary coercible to the opcintype of the unique index column,
9945  * and ri_triggers.c will only deal with PK datums in terms of
9946  * that opcintype. Changing the opcintype also changes pfeqop.
9947  */
9948  old_check_ok = (new_pathtype == old_pathtype &&
9949  new_castfunc == old_castfunc &&
9950  (!IsPolymorphicType(pfeqop_right) ||
9951  new_fktype == old_fktype));
9952  }
9953 
9954  pfeqoperators[i] = pfeqop;
9955  ppeqoperators[i] = ppeqop;
9956  ffeqoperators[i] = ffeqop;
9957  }
9958 
9959  /*
9960  * Create all the constraint and trigger objects, recursing to partitions
9961  * as necessary. First handle the referenced side.
9962  */
9963  address = addFkRecurseReferenced(wqueue, fkconstraint, rel, pkrel,
9964  indexOid,
9965  InvalidOid, /* no parent constraint */
9966  numfks,
9967  pkattnum,
9968  fkattnum,
9969  pfeqoperators,
9970  ppeqoperators,
9971  ffeqoperators,
9972  numfkdelsetcols,
9973  fkdelsetcols,
9974  old_check_ok,
9976 
9977  /* Now handle the referencing side. */
9978  addFkRecurseReferencing(wqueue, fkconstraint, rel, pkrel,
9979  indexOid,
9980  address.objectId,
9981  numfks,
9982  pkattnum,
9983  fkattnum,
9984  pfeqoperators,
9985  ppeqoperators,
9986  ffeqoperators,
9987  numfkdelsetcols,
9988  fkdelsetcols,
9989  old_check_ok,
9990  lockmode,
9992 
9993  /*
9994  * Done. Close pk table, but keep lock until we've committed.
9995  */
9996  table_close(pkrel, NoLock);
9997 
9998  return address;
9999 }
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
bool allowSystemTableMods
Definition: globals.c:128
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:2726
#define FKCONSTR_ACTION_CASCADE
Definition: parsenodes.h:2724
#define FKCONSTR_ACTION_SETNULL
Definition: parsenodes.h:2725
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:209
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:714
#define RelationIsPermanent(relation)
Definition: rel.h:617
TupleDesc oldDesc
Definition: tablecmds.c:171
List * pk_attrs
Definition: parsenodes.h:2768
List * fk_del_set_cols
Definition: parsenodes.h:2772
Oid old_pktable_oid
Definition: parsenodes.h:2774
List * old_conpfeqop
Definition: parsenodes.h:2773
RangeVar * pktable
Definition: parsenodes.h:2766
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)
Definition: tablecmds.c:11817
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
Definition: tablecmds.c:12055
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids)
Definition: tablecmds.c:11765
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
Definition: tablecmds.c:12084
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, Oid *opclasses)
Definition: tablecmds.c:11916
static void validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, const int16 *fksetcolsattnums, List *fksetcols)
Definition: tablecmds.c:10007
#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(), Constraint::fk_attrs, Constraint::fk_del_action, Constraint::fk_del_set_cols, Constraint::fk_upd_action, FKCONSTR_ACTION_CASCADE, FKCONSTR_ACTION_SETDEFAULT, FKCONSTR_ACTION_SETNULL, format_type_be(), get_opfamily_member(), getBaseType(), GETSTRUCT, HeapTupleIsValid, i, INDEX_MAX_KEYS, Constraint::initially_valid, InvalidOid, IsSystemRelation(), lfirst_oid, list_head(), list_length(), list_nth(), lnext(), NIL, NoLock, ObjectAddress::objectId, ObjectIdGetDatum(), OidIsValid, Constraint::old_conpfeqop, Constraint::old_pktable_oid, AlteredTableInfo::oldDesc, Constraint::pk_attrs, Constraint::pktable, RelationData::rd_islocaltemp, RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RelationIsPermanent, ReleaseSysCache(), 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 6664 of file tablecmds.c.

6665 {
6666  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6667  {
6668  List *inh;
6669  ListCell *cell;
6670 
6671  inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6672  /* first element is the parent rel; must ignore it */
6673  for_each_from(cell, inh, 1)
6674  {
6675  Relation childrel;
6676 
6677  /* find_all_inheritors already got lock */
6678  childrel = table_open(lfirst_oid(cell), NoLock);
6679  CheckAlterTableIsSafe(childrel);
6680  table_close(childrel, NoLock);
6681  }
6682  list_free(inh);
6683  }
6684 }
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
Definition: pg_inherits.c:255
#define for_each_from(cell, lst, N)
Definition: pg_list.h:414

References CheckAlterTableIsSafe(), find_all_inheritors(), for_each_from, lfirst_oid, list_free(), NoLock, RelationData::rd_rel, RelationGetRelid, table_close(), and table_open().

Referenced by ATPrepCmd().

◆ ATColumnChangeRequiresRewrite()

static bool ATColumnChangeRequiresRewrite ( Node expr,
AttrNumber  varattno 
)
static

Definition at line 12960 of file tablecmds.c.

12961 {
12962  Assert(expr != NULL);
12963 
12964  for (;;)
12965  {
12966  /* only one varno, so no need to check that */
12967  if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
12968  return false;
12969  else if (IsA(expr, RelabelType))
12970  expr = (Node *) ((RelabelType *) expr)->arg;
12971  else if (IsA(expr, CoerceToDomain))
12972  {
12973  CoerceToDomain *d = (CoerceToDomain *) expr;
12974 
12976  return true;
12977  expr = (Node *) d->arg;
12978  }
12979  else if (IsA(expr, FuncExpr))
12980  {
12981  FuncExpr *f = (FuncExpr *) expr;
12982 
12983  switch (f->funcid)
12984  {
12985  case F_TIMESTAMPTZ_TIMESTAMP:
12986  case F_TIMESTAMP_TIMESTAMPTZ:
12988  return true;
12989  else
12990  expr = linitial(f->args);
12991  break;
12992  default:
12993  return true;
12994  }
12995  }
12996  else
12997  return true;
12998  }
12999 }
bool TimestampTimestampTzRequiresRewrite(void)
Definition: timestamp.c:6274
void * arg
#define linitial(l)
Definition: pg_list.h:178
Oid funcid
Definition: primnodes.h:750
List * args
Definition: primnodes.h:768
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 4715 of file tablecmds.c.

4718 {
4719  List *wqueue = NIL;
4720  ListCell *lcmd;
4721 
4722  /* Phase 1: preliminary examination of commands, create work queue */
4723  foreach(lcmd, cmds)
4724  {
4725  AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
4726 
4727  ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4728  }
4729 
4730  /* Close the relation, but keep lock until commit */
4731  relation_close(rel, NoLock);
4732 
4733  /* Phase 2: update system catalogs */
4734  ATRewriteCatalogs(&wqueue, lockmode, context);
4735 
4736  /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4737  ATRewriteTables(parsetree, &wqueue, lockmode, context);
4738 }
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5139
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:4750
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:5699

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

19942 {
19943  List *constraints;
19944  ListCell *cell;
19945 
19946  constraints = GetParentedForeignKeyRefs(partition);
19947 
19948  foreach(cell, constraints)
19949  {
19950  Oid constrOid = lfirst_oid(cell);
19951  HeapTuple tuple;
19952  Form_pg_constraint constrForm;
19953  Relation rel;
19954  Trigger trig = {0};
19955 
19956  tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
19957  if (!HeapTupleIsValid(tuple))
19958  elog(ERROR, "cache lookup failed for constraint %u", constrOid);
19959  constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
19960 
19961  Assert(OidIsValid(constrForm->conparentid));
19962  Assert(constrForm->confrelid == RelationGetRelid(partition));
19963 
19964  /* prevent data changes into the referencing table until commit */
19965  rel = table_open(constrForm->conrelid, ShareLock);
19966 
19967  trig.tgoid = InvalidOid;
19968  trig.tgname = NameStr(constrForm->conname);
19970  trig.tgisinternal = true;
19971  trig.tgconstrrelid = RelationGetRelid(partition);
19972  trig.tgconstrindid = constrForm->conindid;
19973  trig.tgconstraint = constrForm->oid;
19974  trig.tgdeferrable = false;
19975  trig.tginitdeferred = false;
19976  /* we needn't fill in remaining fields */
19977 
19978  RI_PartitionRemove_Check(&trig, rel, partition);
19979 
19980  ReleaseSysCache(tuple);
19981 
19982  table_close(rel, NoLock);
19983  }
19984 }
#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:1654
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:19888
#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 17550 of file tablecmds.c.

17552 {
17553  ListCell *cur_item;
17554 
17555  foreach(cur_item, on_commits)
17556  {
17557  OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
17558 
17559  if (!isCommit && oc->creating_subid == mySubid)
17560  {
17561  /* cur_item must be removed */
17563  pfree(oc);
17564  }
17565  else
17566  {
17567  /* cur_item must be preserved */
17568  if (oc->creating_subid == mySubid)
17569  oc->creating_subid = parentSubid;
17570  if (oc->deleting_subid == mySubid)
17571  oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
17572  }
17573  }
17574 }
#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 17518 of file tablecmds.c.

17519 {
17520  ListCell *cur_item;
17521 
17522  foreach(cur_item, on_commits)
17523  {
17524  OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
17525 
17526  if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
17528  {
17529  /* cur_item must be removed */
17531  pfree(oc);
17532  }
17533  else
17534  {
17535  /* cur_item must be preserved */
17538  }
17539  }
17540 }

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

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

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(), CheckAlterTableIsSafe(), CheckAttributeType(), 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 9330 of file tablecmds.c.

9333 {
9335 
9336  Assert(IsA(newConstraint, Constraint));
9337 
9338  /*
9339  * Currently, we only expect to see CONSTR_CHECK and CONSTR_FOREIGN nodes
9340  * arriving here (see the preprocessing done in parse_utilcmd.c). Use a
9341  * switch anyway to make it easier to add more code later.
9342  */
9343  switch (newConstraint->contype)
9344  {
9345  case CONSTR_CHECK:
9346  address =
9347  ATAddCheckConstraint(wqueue, tab, rel,
9348  newConstraint, recurse, false, is_readd,
9349  lockmode);
9350  break;
9351 
9352  case CONSTR_FOREIGN:
9353 
9354  /*
9355  * Assign or validate constraint name
9356  */
9357  if (newConstraint->conname)
9358  {
9360  RelationGetRelid(rel),
9361  newConstraint->conname))
9362  ereport(ERROR,
9364  errmsg("constraint \"%s\" for relation \"%s\" already exists",
9365  newConstraint->conname,
9366  RelationGetRelationName(rel))));
9367  }
9368  else
9369  newConstraint->conname =
9372  "fkey",
9373  RelationGetNamespace(rel),
9374  NIL);
9375 
9376  address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9377  newConstraint,
9378  recurse, false,
9379  lockmode);
9380  break;
9381 
9382  default:
9383  elog(ERROR, "unrecognized constraint type: %d",
9384  (int) newConstraint->contype);
9385  }
9386 
9387  return address;
9388 }
@ CONSTR_CHECK
Definition: parsenodes.h:2710
#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:9582

References Assert, ATAddCheckConstraint(), ATAddForeignKeyConstraint(), ChooseConstraintName(), ChooseForeignKeyConstraintNameAddition(), Constraint::conname, CONSTR_CHECK, CONSTR_FOREIGN, 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 7998 of file tablecmds.c.

8000 {
8001  Relation attrelation;
8002  HeapTuple tuple;
8003  Form_pg_attribute attTup;
8005  ObjectAddress address;
8006  ColumnDef *cdef = castNode(ColumnDef, def);
8007  bool ispartitioned;
8008 
8009  ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8010  if (ispartitioned && !recurse)
8011  ereport(ERROR,
8012  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8013  errmsg("cannot add identity to a column of only the partitioned table"),
8014  errhint("Do not specify the ONLY keyword.")));
8015 
8016  if (rel->rd_rel->relispartition && !recursing)
8017  ereport(ERROR,
8018  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8019  errmsg("cannot add identity to a column of a partition"));
8020 
8021  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8022 
8023  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8024  if (!HeapTupleIsValid(tuple))
8025  ereport(ERROR,
8026  (errcode(ERRCODE_UNDEFINED_COLUMN),
8027  errmsg("column \"%s\" of relation \"%s\" does not exist",
8028  colName, RelationGetRelationName(rel))));
8029  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8030  attnum = attTup->attnum;
8031 
8032  /* Can't alter a system attribute */
8033  if (attnum <= 0)
8034  ereport(ERROR,
8035  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8036  errmsg("cannot alter system column \"%s\"",
8037  colName)));
8038 
8039  /*
8040  * Creating a column as identity implies NOT NULL, so adding the identity
8041  * to an existing column that is not NOT NULL would create a state that
8042  * cannot be reproduced without contortions.
8043  */
8044  if (!attTup->attnotnull)
8045  ereport(ERROR,
8046  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8047  errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8048  colName, RelationGetRelationName(rel))));
8049 
8050  if (attTup->attidentity)
8051  ereport(ERROR,
8052  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8053  errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8054  colName, RelationGetRelationName(rel))));
8055 
8056  if (attTup->atthasdef)
8057  ereport(ERROR,
8058  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8059  errmsg("column \"%s\" of relation \"%s\" already has a default value",
8060  colName, RelationGetRelationName(rel))));
8061 
8062  attTup->attidentity = cdef->identity;
8063  CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8064 
8065  InvokeObjectPostAlterHook(RelationRelationId,
8066  RelationGetRelid(rel),
8067  attTup->attnum);
8068  ObjectAddressSubSet(address, RelationRelationId,
8069  RelationGetRelid(rel), attnum);
8070  heap_freetuple(tuple);
8071 
8072  table_close(attrelation, RowExclusiveLock);
8073 
8074  /*
8075  * Recurse to propagate the identity column to partitions. Identity is
8076  * not inherited in regular inheritance children.
8077  */
8078  if (recurse && ispartitioned)
8079  {
8080  List *children;
8081  ListCell *lc;
8082 
8083  children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8084 
8085  foreach(lc, children)
8086  {
8087  Relation childrel;
8088 
8089  childrel = table_open(lfirst_oid(lc), NoLock);
8090  ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8091  table_close(childrel, NoLock);
8092  }
8093  }
8094 
8095  return address;
8096 }
int errhint(const char *fmt,...)
Definition: elog.c:1317
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:7998

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

9156 {
9157  bool check_rights;
9158  bool skip_build;
9159  bool quiet;
9160  ObjectAddress address;
9161 
9162  Assert(IsA(stmt, IndexStmt));
9163  Assert(!stmt->concurrent);
9164 
9165  /* The IndexStmt has already been through transformIndexStmt */
9166  Assert(stmt->transformed);
9167 
9168  /* suppress schema rights check when rebuilding existing index */
9169  check_rights = !is_rebuild;
9170  /* skip index build if phase 3 will do it or we're reusing an old one */
9171  skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9172  /* suppress notices when rebuilding existing index */
9173  quiet = is_rebuild;
9174 
9175  address = DefineIndex(RelationGetRelid(rel),
9176  stmt,
9177  InvalidOid, /* no predefined OID */
9178  InvalidOid, /* no parent index */
9179  InvalidOid, /* no parent constraint */
9180  -1, /* total_parts unknown */
9181  true, /* is_alter_table */
9182  check_rights,
9183  false, /* check_not_in_use - we did it already */
9184  skip_build,
9185  quiet);
9186 
9187  /*
9188  * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9189  * new index instead of building from scratch. Restore associated fields.
9190  * This may store InvalidSubTransactionId in both fields, in which case
9191  * relcache.c will assume it can rebuild the relcache entry. Hence, do
9192  * this after the CCI that made catalog rows visible to any rebuild. The
9193  * DROP of the old edition of this index will have scheduled the storage
9194  * for deletion at commit, so cancel that pending deletion.
9195  */
9196  if (RelFileNumberIsValid(stmt->oldNumber))
9197  {
9198  Relation irel = index_open(address.objectId, NoLock);
9199 
9200  irel->rd_createSubid = stmt->oldCreateSubid;
9201  irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9202  RelationPreserveStorage(irel->rd_locator, true);
9203  index_close(irel, NoLock);
9204  }
9205 
9206  return address;
9207 }
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:531
#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 9238 of file tablecmds.c.

9240 {
9241  Oid index_oid = stmt->indexOid;
9242  Relation indexRel;
9243  char *indexName;
9244  IndexInfo *indexInfo;
9245  char *constraintName;
9246  char constraintType;
9247  ObjectAddress address;
9248  bits16 flags;
9249 
9250  Assert(IsA(stmt, IndexStmt));
9251  Assert(OidIsValid(index_oid));
9252  Assert(stmt->isconstraint);
9253 
9254  /*
9255  * Doing this on partitioned tables is not a simple feature to implement,
9256  * so let's punt for now.
9257  */
9258  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9259  ereport(ERROR,
9260  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9261  errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9262 
9263  indexRel = index_open(index_oid, AccessShareLock);
9264 
9265  indexName = pstrdup(RelationGetRelationName(indexRel));
9266 
9267  indexInfo = BuildIndexInfo(indexRel);
9268 
9269  /* this should have been checked at parse time */
9270  if (!indexInfo->ii_Unique)
9271  elog(ERROR, "index \"%s\" is not unique", indexName);
9272 
9273  /*
9274  * Determine name to assign to constraint. We require a constraint to
9275  * have the same name as the underlying index; therefore, use the index's
9276  * existing name as the default constraint name, and if the user
9277  * explicitly gives some other name for the constraint, rename the index
9278  * to match.
9279  */
9280  constraintName = stmt->idxname;
9281  if (constraintName == NULL)
9282  constraintName = indexName;
9283  else if (strcmp(constraintName, indexName) != 0)
9284  {
9285  ereport(NOTICE,
9286  (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9287  indexName, constraintName)));
9288  RenameRelationInternal(index_oid, constraintName, false, true);
9289  }
9290 
9291  /* Extra checks needed if making primary key */
9292  if (stmt->primary)
9293  index_check_primary_key(rel, indexInfo, true, stmt);
9294 
9295  /* Note we currently don't support EXCLUSION constraints here */
9296  if (stmt->primary)
9297  constraintType = CONSTRAINT_PRIMARY;
9298  else
9299  constraintType = CONSTRAINT_UNIQUE;
9300 
9301  /* Create the catalog entries for the constraint */
9304  (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9305  (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9306  (stmt->primary ? INDEX_CONSTR_CREATE_MARK_AS_PRIMARY : 0);
9307 
9308  address = index_constraint_create(rel,
9309  index_oid,
9310  InvalidOid,
9311  indexInfo,
9312  constraintName,
9313  constraintType,
9314  flags,
9316  false); /* is_internal */
9317 
9318  index_close(indexRel, NoLock);
9319 
9320  return address;
9321 }
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:2404
#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:1696
bool ii_Unique
Definition: execnodes.h:197
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition: tablecmds.c:4101

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

15499 {
15500  Relation parent_rel;
15501  List *children;
15502  ObjectAddress address;
15503  const char *trigger_name;
15504 
15505  /*
15506  * A self-exclusive lock is needed here. See the similar case in
15507  * MergeAttributes() for a full explanation.
15508  */
15509  parent_rel = table_openrv(parent, ShareUpdateExclusiveLock);
15510 
15511  /*
15512  * Must be owner of both parent and child -- child was checked by
15513  * ATSimplePermissions call in ATPrepCmd
15514  */
15516 
15517  /* Permanent rels cannot inherit from temporary ones */
15518  if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15519  child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
15520  ereport(ERROR,
15521  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15522  errmsg("cannot inherit from temporary relation \"%s\"",
15523  RelationGetRelationName(parent_rel))));
15524 
15525  /* If parent rel is temp, it must belong to this session */
15526  if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15527  !parent_rel->rd_islocaltemp)
15528  ereport(ERROR,
15529  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15530  errmsg("cannot inherit from temporary relation of another session")));
15531 
15532  /* Ditto for the child */
15533  if (child_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
15534  !child_rel->rd_islocaltemp)
15535  ereport(ERROR,
15536  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15537  errmsg("cannot inherit to temporary relation of another session")));
15538 
15539  /* Prevent partitioned tables from becoming inheritance parents */
15540  if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15541  ereport(ERROR,
15542  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15543  errmsg("cannot inherit from partitioned table \"%s\"",
15544  parent->relname)));
15545 
15546  /* Likewise for partitions */
15547  if (parent_rel->rd_rel->relispartition)
15548  ereport(ERROR,
15549  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15550  errmsg("cannot inherit from a partition")));
15551 
15552  /*
15553  * Prevent circularity by seeing if proposed parent inherits from child.
15554  * (In particular, this disallows making a rel inherit from itself.)
15555  *
15556  * This is not completely bulletproof because of race conditions: in
15557  * multi-level inheritance trees, someone else could concurrently be
15558  * making another inheritance link that closes the loop but does not join
15559  * either of the rels we have locked. Preventing that seems to require
15560  * exclusive locks on the entire inheritance tree, which is a cure worse
15561  * than the disease. find_all_inheritors() will cope with circularity
15562  * anyway, so don't sweat it too much.
15563  *
15564  * We use weakest lock we can on child's children, namely AccessShareLock.
15565  */
15566  children = find_all_inheritors(RelationGetRelid(child_rel),
15567  AccessShareLock, NULL);
15568 
15569  if (list_member_oid(children, RelationGetRelid(parent_rel)))
15570  ereport(ERROR,
15571  (errcode(ERRCODE_DUPLICATE_TABLE),
15572  errmsg("circular inheritance not allowed"),
15573  errdetail("\"%s\" is already a child of \"%s\".",
15574  parent->relname,
15575  RelationGetRelationName(child_rel))));
15576 
15577  /*
15578  * If child_rel has row-level triggers with transition tables, we
15579  * currently don't allow it to become an inheritance child. See also
15580  * prohibitions in ATExecAttachPartition() and CreateTrigger().
15581  */
15582  trigger_name = FindTriggerIncompatibleWithInheritance(child_rel->trigdesc);
15583  if (trigger_name != NULL)
15584  ereport(ERROR,
15585  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
15586  errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
15587  trigger_name, RelationGetRelationName(child_rel)),
15588  errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
15589 
15590  /* OK to create inheritance */
15591  CreateInheritance(child_rel, parent_rel, false);
15592 
15593  ObjectAddressSet(address, RelationRelationId,
15594  RelationGetRelid(parent_rel));
15595 
15596  /* keep our lock on the parent relation until commit */
15597  table_close(parent_rel, NoLock);
15598 
15599  return address;
15600 }
char * relname
Definition: primnodes.h:82
TriggerDesc * trigdesc
Definition: rel.h:117
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
Definition: tablecmds.c:15610
const char * FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc)
Definition: trigger.c:2272

References AccessShareLock, AT_AddInherit, 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 16323 of file tablecmds.c.

16324 {
16325  Oid relid = RelationGetRelid(rel);
16326  Type typetuple;
16327  Form_pg_type typeform;
16328  Oid typeid;
16329  Relation inheritsRelation,
16330  relationRelation;
16331  SysScanDesc scan;
16332  ScanKeyData key;
16333  AttrNumber table_attno,
16334  type_attno;
16335  TupleDesc typeTupleDesc,
16336  tableTupleDesc;
16337  ObjectAddress tableobj,
16338  typeobj;
16339  HeapTuple classtuple;
16340 
16341  /* Validate the type. */
16342  typetuple = typenameType(NULL, ofTypename, NULL);
16343  check_of_type(typetuple);
16344  typeform = (Form_pg_type) GETSTRUCT(typetuple);
16345  typeid = typeform->oid;
16346 
16347  /* Fail if the table has any inheritance parents. */
16348  inheritsRelation = table_open(InheritsRelationId, AccessShareLock);
16349  ScanKeyInit(&key,
16350  Anum_pg_inherits_inhrelid,
16351  BTEqualStrategyNumber, F_OIDEQ,
16352  ObjectIdGetDatum(relid));
16353  scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
16354  true, NULL, 1, &key);
16356  ereport(ERROR,
16357  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16358  errmsg("typed tables cannot inherit")));
16359  systable_endscan(scan);
16360  table_close(inheritsRelation, AccessShareLock);
16361 
16362  /*
16363  * Check the tuple descriptors for compatibility. Unlike inheritance, we
16364  * require that the order also match. However, attnotnull need not match.
16365  */
16366  typeTupleDesc = lookup_rowtype_tupdesc(typeid, -1);
16367  tableTupleDesc = RelationGetDescr(rel);
16368  table_attno = 1;
16369  for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
16370  {
16371  Form_pg_attribute type_attr,
16372  table_attr;
16373  const char *type_attname,
16374  *table_attname;
16375 
16376  /* Get the next non-dropped type attribute. */
16377  type_attr = TupleDescAttr(typeTupleDesc, type_attno - 1);
16378  if (type_attr->attisdropped)
16379  continue;
16380  type_attname = NameStr(type_attr->attname);
16381 
16382  /* Get the next non-dropped table attribute. */
16383  do
16384  {
16385  if (table_attno > tableTupleDesc->natts)
16386  ereport(ERROR,
16387  (errcode(ERRCODE_DATATYPE_MISMATCH),
16388  errmsg("table is missing column \"%s\"",
16389  type_attname)));
16390  table_attr = TupleDescAttr(tableTupleDesc, table_attno - 1);
16391  table_attno++;
16392  } while (table_attr->attisdropped);
16393  table_attname = NameStr(table_attr->attname);
16394 
16395  /* Compare name. */
16396  if (strncmp(table_attname, type_attname, NAMEDATALEN) != 0)
16397  ereport(ERROR,
16398  (errcode(ERRCODE_DATATYPE_MISMATCH),
16399  errmsg("table has column \"%s\" where type requires \"%s\"",
16400  table_attname, type_attname)));
16401 
16402  /* Compare type. */
16403  if (table_attr->atttypid != type_attr->atttypid ||
16404  table_attr->atttypmod != type_attr->atttypmod ||
16405  table_attr->attcollation != type_attr->attcollation)
16406  ereport(ERROR,
16407  (errcode(ERRCODE_DATATYPE_MISMATCH),
16408  errmsg("table \"%s\" has different type for column \"%s\"",
16409  RelationGetRelationName(rel), type_attname)));
16410  }
16411  ReleaseTupleDesc(typeTupleDesc);
16412 
16413  /* Any remaining columns at the end of the table had better be dropped. */
16414  for (; table_attno <= tableTupleDesc->natts; table_attno++)
16415  {
16416  Form_pg_attribute table_attr = TupleDescAttr(tableTupleDesc,
16417  table_attno - 1);
16418 
16419  if (!table_attr->attisdropped)
16420  ereport(ERROR,
16421  (errcode(ERRCODE_DATATYPE_MISMATCH),
16422  errmsg("table has extra column \"%s\"",
16423  NameStr(table_attr->attname))));
16424  }
16425 
16426  /* If the table was already typed, drop the existing dependency. */
16427  if (rel->rd_rel->reloftype)
16428  drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
16430 
16431  /* Record a dependency on the new type. */
16432  tableobj.classId = RelationRelationId;
16433  tableobj.objectId = relid;
16434  tableobj.objectSubId = 0;
16435  typeobj.classId = TypeRelationId;
16436  typeobj.objectId = typeid;
16437  typeobj.objectSubId = 0;
16438  recordDependencyOn(&tableobj, &typeobj, DEPENDENCY_NORMAL);
16439 
16440  /* Update pg_class.reloftype */
16441  relationRelation = table_open(RelationRelationId, RowExclusiveLock);
16442  classtuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16443  if (!HeapTupleIsValid(classtuple))
16444  elog(ERROR, "cache lookup failed for relation %u", relid);
16445  ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
16446  CatalogTupleUpdate(relationRelation, &classtuple->t_self, classtuple);
16447 
16448  InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
16449 
16450  heap_freetuple(classtuple);
16451  table_close(relationRelation, RowExclusiveLock);
16452 
16453  ReleaseSysCache(typetuple);
16454 
16455  return typeobj;
16456 }
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:16271
void check_of_type(HeapTuple typetuple)
Definition: tablecmds.c:6946
#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 9217 of file tablecmds.c.

9219 {
9220  ObjectAddress address;
9221 
9223 
9224  /* The CreateStatsStmt has already been through transformStatsStmt */
9225  Assert(stmt->transformed);
9226 
9227  address = CreateStatistics(stmt);
9228 
9229  return address;
9230 }
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 14197 of file tablecmds.c.

14201 {
14202  Relation ftrel;
14203  Relation attrel;
14204  ForeignServer *server;
14205  ForeignDataWrapper *fdw;
14206  HeapTuple tuple;
14207  HeapTuple newtuple;
14208  bool isnull;
14209  Datum repl_val[Natts_pg_attribute];
14210  bool repl_null[Natts_pg_attribute];
14211  bool repl_repl[Natts_pg_attribute];
14212  Datum datum;
14213  Form_pg_foreign_table fttableform;
14214  Form_pg_attribute atttableform;
14216  ObjectAddress address;
14217 
14218  if (options == NIL)
14219  return InvalidObjectAddress;
14220 
14221  /* First, determine FDW validator associated to the foreign table. */
14222  ftrel = table_open(ForeignTableRelationId, AccessShareLock);
14223  tuple = SearchSysCache1(FOREIGNTABLEREL, ObjectIdGetDatum(rel->rd_id));
14224  if (!HeapTupleIsValid(tuple))
14225  ereport(ERROR,
14226  (errcode(ERRCODE_UNDEFINED_OBJECT),
14227  errmsg("foreign table \"%s\" does not exist",
14228  RelationGetRelationName(rel))));
14229  fttableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
14230  server = GetForeignServer(fttableform->ftserver);
14231  fdw = GetForeignDataWrapper(server->fdwid);
14232 
14233  table_close(ftrel, AccessShareLock);
14234  ReleaseSysCache(tuple);
14235 
14236  attrel = table_open(AttributeRelationId, RowExclusiveLock);
14237  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
14238  if (!HeapTupleIsValid(tuple))
14239  ereport(ERROR,
14240  (errcode(ERRCODE_UNDEFINED_COLUMN),
14241  errmsg("column \"%s\" of relation \"%s\" does not exist",
14242  colName, RelationGetRelationName(rel))));
14243 
14244  /* Prevent them from altering a system attribute */
14245  atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
14246  attnum = atttableform->attnum;
14247  if (attnum <= 0)
14248  ereport(ERROR,
14249  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14250  errmsg("cannot alter system column \"%s\"", colName)));
14251 
14252 
14253  /* Initialize buffers for new tuple values */
14254  memset(repl_val, 0, sizeof(repl_val));
14255  memset(repl_null, false, sizeof(repl_null));
14256  memset(repl_repl, false, sizeof(repl_repl));
14257 
14258  /* Extract the current options */
14259  datum = SysCacheGetAttr(ATTNAME,
14260  tuple,
14261  Anum_pg_attribute_attfdwoptions,
14262  &isnull);
14263  if (isnull)
14264  datum = PointerGetDatum(NULL);
14265 
14266  /* Transform the options */
14267  datum = transformGenericOptions(AttributeRelationId,
14268  datum,
14269  options,
14270  fdw->fdwvalidator);
14271 
14272  if (PointerIsValid(DatumGetPointer(datum)))
14273  repl_val[Anum_pg_attribute_attfdwoptions - 1] = datum;
14274  else
14275  repl_null[Anum_pg_attribute_attfdwoptions - 1] = true;
14276 
14277  repl_repl[Anum_pg_attribute_attfdwoptions - 1] = true;
14278 
14279  /* Everything looks good - update the tuple */
14280 
14281  newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
14282  repl_val, repl_null, repl_repl);
14283 
14284  CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
14285 
14286  InvokeObjectPostAlterHook(RelationRelationId,
14287  RelationGetRelid(rel),
14288  atttableform->attnum);
14289  ObjectAddressSubSet(address, RelationRelationId,
14290  RelationGetRelid(rel), attnum);
14291 
14292  ReleaseSysCache(tuple);
14293 
14294  table_close(attrel, RowExclusiveLock);
14295 
14296  heap_freetuple(newtuple);
14297 
14298  return address;
14299 }
#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 13007 of file tablecmds.c.

13009 {
13010  char *colName = cmd->name;
13011  ColumnDef *def = (ColumnDef *) cmd->def;
13012  TypeName *typeName = def->typeName;
13013  HeapTuple heapTup;
13014  Form_pg_attribute attTup,
13015  attOldTup;
13017  HeapTuple typeTuple;
13018  Form_pg_type tform;
13019  Oid targettype;
13020  int32 targettypmod;
13021  Oid targetcollid;
13022  Node *defaultexpr;
13023  Relation attrelation;
13024  Relation depRel;
13025  ScanKeyData key[3];
13026  SysScanDesc scan;
13027  HeapTuple depTup;
13028  ObjectAddress address;
13029 
13030  /*
13031  * Clear all the missing values if we're rewriting the table, since this
13032  * renders them pointless.
13033  */
13034  if (tab->rewrite)
13035  {
13036  Relation newrel;
13037 
13038  newrel = table_open(RelationGetRelid(rel), NoLock);
13039  RelationClearMissing(newrel);
13040  relation_close(newrel, NoLock);
13041  /* make sure we don't conflict with later attribute modifications */
13043  }
13044 
13045  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
13046 
13047  /* Look up the target column */
13048  heapTup = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
13049  if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
13050  ereport(ERROR,
13051  (errcode(ERRCODE_UNDEFINED_COLUMN),
13052  errmsg("column \"%s\" of relation \"%s\" does not exist",
13053  colName, RelationGetRelationName(rel))));
13054  attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13055  attnum = attTup->attnum;
13056  attOldTup = TupleDescAttr(tab->oldDesc, attnum - 1);
13057 
13058  /* Check for multiple ALTER TYPE on same column --- can't cope */
13059  if (attTup->atttypid != attOldTup->atttypid ||
13060  attTup->atttypmod != attOldTup->atttypmod)
13061  ereport(ERROR,
13062  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13063  errmsg("cannot alter type of column \"%s\" twice",
13064  colName)));
13065 
13066  /* Look up the target type (should not fail, since prep found it) */
13067  typeTuple = typenameType(NULL, typeName, &targettypmod);
13068  tform = (Form_pg_type) GETSTRUCT(typeTuple);
13069  targettype = tform->oid;
13070  /* And the collation */
13071  targetcollid = GetColumnDefCollation(NULL, def, targettype);
13072 
13073  /*
13074  * If there is a default expression for the column, get it and ensure we
13075  * can coerce it to the new datatype. (We must do this before changing
13076  * the column type, because build_column_default itself will try to
13077  * coerce, and will not issue the error message we want if it fails.)
13078  *
13079  * We remove any implicit coercion steps at the top level of the old
13080  * default expression; this has been agreed to satisfy the principle of
13081  * least surprise. (The conversion to the new column type should act like
13082  * it started from what the user sees as the stored expression, and the
13083  * implicit coercions aren't going to be shown.)
13084  */
13085  if (attTup->atthasdef)
13086  {
13087  defaultexpr = build_column_default(rel, attnum);
13088  Assert(defaultexpr);
13089  defaultexpr = strip_implicit_coercions(defaultexpr);
13090  defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
13091  defaultexpr, exprType(defaultexpr),
13092  targettype, targettypmod,
13095  -1);
13096  if (defaultexpr == NULL)
13097  {
13098  if (attTup->attgenerated)
13099  ereport(ERROR,
13100  (errcode(ERRCODE_DATATYPE_MISMATCH),
13101  errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
13102  colName, format_type_be(targettype))));
13103  else
13104  ereport(ERROR,
13105  (errcode(ERRCODE_DATATYPE_MISMATCH),
13106  errmsg("default for column \"%s\" cannot be cast automatically to type %s",
13107  colName, format_type_be(targettype))));
13108  }
13109  }
13110  else
13111  defaultexpr = NULL;
13112 
13113  /*
13114  * Find everything that depends on the column (constraints, indexes, etc),
13115  * and record enough information to let us recreate the objects.
13116  *
13117  * The actual recreation does not happen here, but only after we have
13118  * performed all the individual ALTER TYPE operations. We have to save
13119  * the info before executing ALTER TYPE, though, else the deparser will
13120  * get confused.
13121  */
13123 
13124  /*
13125  * Now scan for dependencies of this column on other things. The only
13126  * things we should find are the dependency on the column datatype and
13127  * possibly a collation dependency. Those can be removed.
13128  */
13129  depRel = table_open(DependRelationId, RowExclusiveLock);
13130 
13131  ScanKeyInit(&key[0],
13132  Anum_pg_depend_classid,
13133  BTEqualStrategyNumber, F_OIDEQ,
13134  ObjectIdGetDatum(RelationRelationId));
13135  ScanKeyInit(&key[1],
13136  Anum_pg_depend_objid,
13137  BTEqualStrategyNumber, F_OIDEQ,
13139  ScanKeyInit(&key[2],
13140  Anum_pg_depend_objsubid,
13141  BTEqualStrategyNumber, F_INT4EQ,
13143 
13144  scan = systable_beginscan(depRel, DependDependerIndexId, true,
13145  NULL, 3, key);
13146 
13147  while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13148  {
13149  Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13150  ObjectAddress foundObject;
13151 
13152  foundObject.classId = foundDep->refclassid;
13153  foundObject.objectId = foundDep->refobjid;
13154  foundObject.objectSubId = foundDep->refobjsubid;
13155 
13156  if (foundDep->deptype != DEPENDENCY_NORMAL)
13157  elog(ERROR, "found unexpected dependency type '%c'",
13158  foundDep->deptype);
13159  if (!(foundDep->refclassid == TypeRelationId &&
13160  foundDep->refobjid == attTup->atttypid) &&
13161  !(foundDep->refclassid == CollationRelationId &&
13162  foundDep->refobjid == attTup->attcollation))
13163  elog(ERROR, "found unexpected dependency for column: %s",
13164  getObjectDescription(&foundObject, false));
13165 
13166  CatalogTupleDelete(depRel, &depTup->t_self);
13167  }
13168 
13169  systable_endscan(scan);
13170 
13171  table_close(depRel, RowExclusiveLock);
13172 
13173  /*
13174  * Here we go --- change the recorded column type and collation. (Note
13175  * heapTup is a copy of the syscache entry, so okay to scribble on.) First
13176  * fix up the missing value if any.
13177  */
13178  if (attTup->atthasmissing)
13179  {
13180  Datum missingval;
13181  bool missingNull;
13182 
13183  /* if rewrite is true the missing value should already be cleared */
13184  Assert(tab->rewrite == 0);
13185 
13186  /* Get the missing value datum */
13187  missingval = heap_getattr(heapTup,
13188  Anum_pg_attribute_attmissingval,
13189  attrelation->rd_att,
13190  &missingNull);
13191 
13192  /* if it's a null array there is nothing to do */
13193 
13194  if (!missingNull)
13195  {
13196  /*
13197  * Get the datum out of the array and repack it in a new array
13198  * built with the new type data. We assume that since the table
13199  * doesn't need rewriting, the actual Datum doesn't need to be
13200  * changed, only the array metadata.
13201  */
13202 
13203  int one = 1;
13204  bool isNull;
13205  Datum valuesAtt[Natts_pg_attribute] = {0};
13206  bool nullsAtt[Natts_pg_attribute] = {0};
13207  bool replacesAtt[Natts_pg_attribute] = {0};
13208  HeapTuple newTup;
13209 
13210  missingval = array_get_element(missingval,
13211  1,
13212  &one,
13213  0,
13214  attTup->attlen,
13215  attTup->attbyval,
13216  attTup->attalign,
13217  &isNull);
13218  missingval = PointerGetDatum(construct_array(&missingval,
13219  1,
13220  targettype,
13221  tform->typlen,
13222  tform->typbyval,
13223  tform->typalign));
13224 
13225  valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
13226  replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
13227  nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
13228 
13229  newTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
13230  valuesAtt, nullsAtt, replacesAtt);
13231  heap_freetuple(heapTup);
13232  heapTup = newTup;
13233  attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
13234  }
13235  }
13236 
13237  attTup->atttypid = targettype;
13238  attTup->atttypmod = targettypmod;
13239  attTup->attcollation = targetcollid;
13240  if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
13241  ereport(ERROR,
13242  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
13243  errmsg("too many array dimensions"));
13244  attTup->attndims = list_length(typeName->arrayBounds);
13245  attTup->attlen = tform->typlen;
13246  attTup->attbyval = tform->typbyval;
13247  attTup->attalign = tform->typalign;
13248  attTup->attstorage = tform->typstorage;
13249  attTup->attcompression = InvalidCompressionMethod;
13250 
13251  ReleaseSysCache(typeTuple);
13252 
13253  CatalogTupleUpdate(attrelation, &heapTup->t_self, heapTup);
13254 
13255  table_close(attrelation, RowExclusiveLock);
13256 
13257  /* Install dependencies on new datatype and collation */
13260 
13261  /*
13262  * Drop any pg_statistic entry for the column, since it's now wrong type
13263  */
13265 
13266  InvokeObjectPostAlterHook(RelationRelationId,
13267  RelationGetRelid(rel), attnum);
13268 
13269  /*
13270  * Update the default, if present, by brute force --- remove and re-add
13271  * the default. Probably unsafe to take shortcuts, since the new version
13272  * may well have additional dependencies. (It's okay to do this now,
13273  * rather than after other ALTER TYPE commands, since the default won't
13274  * depend on other column types.)
13275  */
13276  if (defaultexpr)
13277  {
13278  /*
13279  * If it's a GENERATED default, drop its dependency records, in
13280  * particular its INTERNAL dependency on the column, which would
13281  * otherwise cause dependency.c to refuse to perform the deletion.
13282  */
13283  if (attTup->attgenerated)
13284  {
13285  Oid attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
13286 
13287  if (!OidIsValid(attrdefoid))
13288  elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
13289  RelationGetRelid(rel), attnum);
13290  (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
13291  }
13292 
13293  /*
13294  * Make updates-so-far visible, particularly the new pg_attribute row
13295  * which will be updated again.
13296  */
13298 
13299  /*
13300  * We use RESTRICT here for safety, but at present we do not expect
13301  * anything to depend on the default.
13302  */
13304  true);
13305 
13306  StoreAttrDefault(rel, attnum, defaultexpr, true, false);
13307  }
13308 
13309  ObjectAddressSubSet(address, RelationRelationId,
13310  RelationGetRelid(rel), attnum);
13311 
13312  /* Cleanup */
13313  heap_freetuple(heapTup);
13314 
13315  return address;
13316 }
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3361
Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
Definition: arrayfuncs.c:1820
#define PG_INT16_MAX
Definition: c.h:586
void RelationClearMissing(Relation rel)
Definition: heap.c:1945
void RemoveStatistics(Oid relid, AttrNumber attnum)
Definition: heap.c:2923
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:2334
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:302
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:13324
#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 11287 of file tablecmds.c.

11289 {
11290  Constraint *cmdcon;
11291  Relation conrel;
11292  Relation tgrel;
11293  SysScanDesc scan;
11294  ScanKeyData skey[3];
11295  HeapTuple contuple;
11296  Form_pg_constraint currcon;
11297  ObjectAddress address;
11298  List *otherrelids = NIL;
11299  ListCell *lc;
11300 
11301  cmdcon = castNode(Constraint, cmd->def);
11302 
11303  conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11304  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
11305 
11306  /*
11307  * Find and check the target constraint
11308  */
11309  ScanKeyInit(&skey[0],
11310  Anum_pg_constraint_conrelid,
11311  BTEqualStrategyNumber, F_OIDEQ,
11313  ScanKeyInit(&skey[1],
11314  Anum_pg_constraint_contypid,
11315  BTEqualStrategyNumber, F_OIDEQ,
11317  ScanKeyInit(&skey[2],
11318  Anum_pg_constraint_conname,
11319  BTEqualStrategyNumber, F_NAMEEQ,
11320  CStringGetDatum(cmdcon->conname));
11321  scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11322  true, NULL, 3, skey);
11323 
11324  /* There can be at most one matching row */
11325  if (!HeapTupleIsValid(contuple = systable_getnext(scan)))
11326  ereport(ERROR,
11327  (errcode(ERRCODE_UNDEFINED_OBJECT),
11328  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11329  cmdcon->conname, RelationGetRelationName(rel))));
11330 
11331  currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11332  if (currcon->contype != CONSTRAINT_FOREIGN)
11333  ereport(ERROR,
11334  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11335  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
11336  cmdcon->conname, RelationGetRelationName(rel))));
11337 
11338  /*
11339  * If it's not the topmost constraint, raise an error.
11340  *
11341  * Altering a non-topmost constraint leaves some triggers untouched, since
11342  * they are not directly connected to this constraint; also, pg_dump would
11343  * ignore the deferrability status of the individual constraint, since it
11344  * only dumps topmost constraints. Avoid these problems by refusing this
11345  * operation and telling the user to alter the parent constraint instead.
11346  */
11347  if (OidIsValid(currcon->conparentid))
11348  {
11349  HeapTuple tp;
11350  Oid parent = currcon->conparentid;
11351  char *ancestorname = NULL;
11352  char *ancestortable = NULL;
11353 
11354  /* Loop to find the topmost constraint */
11355  while (HeapTupleIsValid(tp = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parent))))
11356  {
11358 
11359  /* If no parent, this is the constraint we want */
11360  if (!OidIsValid(contup->conparentid))
11361  {
11362  ancestorname = pstrdup(NameStr(contup->conname));
11363  ancestortable = get_rel_name(contup->conrelid);
11364  ReleaseSysCache(tp);
11365  break;
11366  }
11367 
11368  parent = contup->conparentid;
11369  ReleaseSysCache(tp);
11370  }
11371 
11372  ereport(ERROR,
11373  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11374  errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
11375  cmdcon->conname, RelationGetRelationName(rel)),
11376  ancestorname && ancestortable ?
11377  errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
11378  cmdcon->conname, ancestorname, ancestortable) : 0,
11379  errhint("You may alter the constraint it derives from instead.")));
11380  }
11381 
11382  /*
11383  * Do the actual catalog work. We can skip changing if already in the
11384  * desired state, but not if a partitioned table: partitions need to be
11385  * processed regardless, in case they had the constraint locally changed.
11386  */
11387  address = InvalidObjectAddress;
11388  if (currcon->condeferrable != cmdcon->deferrable ||
11389  currcon->condeferred != cmdcon->initdeferred ||
11390  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11391  {
11392  if (ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, rel, contuple,
11393  &otherrelids, lockmode))
11394  ObjectAddressSet(address, ConstraintRelationId, currcon->oid);
11395  }
11396 
11397  /*
11398  * ATExecAlterConstrRecurse already invalidated relcache for the relations
11399  * having the constraint itself; here we also invalidate for relations
11400  * that have any triggers that are part of the constraint.
11401  */
11402  foreach(lc, otherrelids)
11404 
11405  systable_endscan(scan);
11406 
11407  table_close(tgrel, RowExclusiveLock);
11408  table_close(conrel, RowExclusiveLock);
11409 
11410  return address;
11411 }
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:11425

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

11428 {
11429  Form_pg_constraint currcon;
11430  Oid conoid;
11431  Oid refrelid;
11432  bool changed = false;
11433 
11434  /* since this function recurses, it could be driven to stack overflow */
11436 
11437  currcon = (Form_pg_constraint) GETSTRUCT(contuple);
11438  conoid = currcon->oid;
11439  refrelid = currcon->confrelid;
11440 
11441  /*
11442  * Update pg_constraint with the flags from cmdcon.
11443  *
11444  * If called to modify a constraint that's already in the desired state,
11445  * silently do nothing.
11446  */
11447  if (currcon->condeferrable != cmdcon->deferrable ||
11448  currcon->condeferred != cmdcon->initdeferred)
11449  {
11450  HeapTuple copyTuple;
11451  Form_pg_constraint copy_con;
11452  HeapTuple tgtuple;
11453  ScanKeyData tgkey;
11454  SysScanDesc tgscan;
11455 
11456  copyTuple = heap_copytuple(contuple);
11457  copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11458  copy_con->condeferrable = cmdcon->deferrable;
11459  copy_con->condeferred = cmdcon->initdeferred;
11460  CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
11461 
11462  InvokeObjectPostAlterHook(ConstraintRelationId,
11463  conoid, 0);
11464 
11465  heap_freetuple(copyTuple);
11466  changed = true;
11467 
11468  /* Make new constraint flags visible to others */
11470 
11471  /*
11472  * Now we need to update the multiple entries in pg_trigger that
11473  * implement the constraint.
11474  */
11475  ScanKeyInit(&tgkey,
11476  Anum_pg_trigger_tgconstraint,
11477  BTEqualStrategyNumber, F_OIDEQ,
11478  ObjectIdGetDatum(conoid));
11479  tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
11480  NULL, 1, &tgkey);
11481  while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
11482  {
11483  Form_pg_trigger tgform = (Form_pg_trigger) GETSTRUCT(tgtuple);
11484  Form_pg_trigger copy_tg;
11485  HeapTuple tgCopyTuple;
11486 
11487  /*
11488  * Remember OIDs of other relation(s) involved in FK constraint.
11489  * (Note: it's likely that we could skip forcing a relcache inval
11490  * for other rels that don't have a trigger whose properties
11491  * change, but let's be conservative.)
11492  */
11493  if (tgform->tgrelid != RelationGetRelid(rel))
11494  *otherrelids = list_append_unique_oid(*otherrelids,
11495  tgform->tgrelid);
11496 
11497  /*
11498  * Update deferrability of RI_FKey_noaction_del,
11499  * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
11500  * triggers, but not others; see createForeignKeyActionTriggers
11501  * and CreateFKCheckTrigger.
11502  */
11503  if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
11504  tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
11505  tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
11506  tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
11507  continue;
11508 
11509  tgCopyTuple = heap_copytuple(tgtuple);
11510  copy_tg = (Form_pg_trigger) GETSTRUCT(tgCopyTuple);
11511 
11512  copy_tg->tgdeferrable = cmdcon->deferrable;
11513  copy_tg->tginitdeferred = cmdcon->initdeferred;
11514  CatalogTupleUpdate(tgrel, &tgCopyTuple->t_self, tgCopyTuple);
11515 
11516  InvokeObjectPostAlterHook(TriggerRelationId, tgform->oid, 0);
11517 
11518  heap_freetuple(tgCopyTuple);
11519  }
11520 
11521  systable_endscan(tgscan);
11522  }
11523 
11524  /*
11525  * If the table at either end of the constraint is partitioned, we need to
11526  * recurse and handle every constraint that is a child of this one.
11527  *
11528  * (This assumes that the recurse flag is forcibly set for partitioned
11529  * tables, and not set for legacy inheritance, though we don't check for
11530  * that here.)
11531  */
11532  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
11533  get_rel_relkind(refrelid) == RELKIND_PARTITIONED_TABLE)
11534  {
11535  ScanKeyData pkey;
11536  SysScanDesc pscan;
11537  HeapTuple childtup;
11538 
11539  ScanKeyInit(&pkey,
11540  Anum_pg_constraint_conparentid,
11541  BTEqualStrategyNumber, F_OIDEQ,
11542  ObjectIdGetDatum(conoid));
11543 
11544  pscan = systable_beginscan(conrel, ConstraintParentIndexId,
11545  true, NULL, 1, &pkey);
11546 
11547  while (HeapTupleIsValid(childtup = systable_getnext(pscan)))
11548  {
11549  Form_pg_constraint childcon = (Form_pg_constraint) GETSTRUCT(childtup);
11550  Relation childrel;
11551 
11552  childrel = table_open(childcon->conrelid, lockmode);
11553  ATExecAlterConstrRecurse(cmdcon, conrel, tgrel, childrel, childtup,
11554  otherrelids, lockmode);
11555  table_close(childrel, NoLock);
11556  }
11557 
11558  systable_endscan(pscan);
11559  }
11560 
11561  return changed;
11562 }
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 18348 of file tablecmds.c.

18350 {
18351  Relation attachrel,
18352  catalog;
18353  List *attachrel_children;
18354  List *partConstraint;
18355  SysScanDesc scan;
18356  ScanKeyData skey;
18357  AttrNumber attno;
18358  int natts;
18359  TupleDesc tupleDesc;
18360  ObjectAddress address;
18361  const char *trigger_name;
18362  Oid defaultPartOid;
18363  List *partBoundConstraint;
18364  ParseState *pstate = make_parsestate(NULL);
18365 
18366  pstate->p_sourcetext = context->queryString;
18367 
18368  /*
18369  * We must lock the default partition if one exists, because attaching a
18370  * new partition will change its partition constraint.
18371  */
18372  defaultPartOid =
18374  if (OidIsValid(defaultPartOid))
18375  LockRelationOid(defaultPartOid, AccessExclusiveLock);
18376 
18377  attachrel = table_openrv(cmd->name, AccessExclusiveLock);
18378 
18379  /*
18380  * XXX I think it'd be a good idea to grab locks on all tables referenced
18381  * by FKs at this point also.
18382  */
18383 
18384  /*
18385  * Must be owner of both parent and source table -- parent was checked by
18386  * ATSimplePermissions call in ATPrepCmd
18387  */
18389 
18390  /* A partition can only have one parent */
18391  if (attachrel->rd_rel->relispartition)
18392  ereport(ERROR,
18393  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18394  errmsg("\"%s\" is already a partition",
18395  RelationGetRelationName(attachrel))));
18396 
18397  if (OidIsValid(attachrel->rd_rel->reloftype))
18398  ereport(ERROR,
18399  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18400  errmsg("cannot attach a typed table as partition")));
18401 
18402  /*
18403  * Table being attached should not already be part of inheritance; either
18404  * as a child table...
18405  */
18406  catalog = table_open(InheritsRelationId, AccessShareLock);
18407  ScanKeyInit(&skey,
18408  Anum_pg_inherits_inhrelid,
18409  BTEqualStrategyNumber, F_OIDEQ,
18410  ObjectIdGetDatum(RelationGetRelid(attachrel)));
18411  scan = systable_beginscan(catalog, InheritsRelidSeqnoIndexId, true,
18412  NULL, 1, &skey);
18414  ereport(ERROR,
18415  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18416  errmsg("cannot attach inheritance child as partition")));
18417  systable_endscan(scan);
18418 
18419  /* ...or as a parent table (except the case when it is partitioned) */
18420  ScanKeyInit(&skey,
18421  Anum_pg_inherits_inhparent,
18422  BTEqualStrategyNumber, F_OIDEQ,
18423  ObjectIdGetDatum(RelationGetRelid(attachrel)));
18424  scan = systable_beginscan(catalog, InheritsParentIndexId, true, NULL,
18425  1, &skey);
18426  if (HeapTupleIsValid(systable_getnext(scan)) &&
18427  attachrel->rd_rel->relkind == RELKIND_RELATION)
18428  ereport(ERROR,
18429  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18430  errmsg("cannot attach inheritance parent as partition")));
18431  systable_endscan(scan);
18432  table_close(catalog, AccessShareLock);
18433 
18434  /*
18435  * Prevent circularity by seeing if rel is a partition of attachrel. (In
18436  * particular, this disallows making a rel a partition of itself.)
18437  *
18438  * We do that by checking if rel is a member of the list of attachrel's
18439  * partitions provided the latter is partitioned at all. We want to avoid
18440  * having to construct this list again, so we request the strongest lock
18441  * on all partitions. We need the strongest lock, because we may decide
18442  * to scan them if we find out that the table being attached (or its leaf
18443  * partitions) may contain rows that violate the partition constraint. If
18444  * the table has a constraint that would prevent such rows, which by
18445  * definition is present in all the partitions, we need not scan the
18446  * table, nor its partitions. But we cannot risk a deadlock by taking a
18447  * weaker lock now and the stronger one only when needed.
18448  */
18449  attachrel_children = find_all_inheritors(RelationGetRelid(attachrel),
18450  AccessExclusiveLock, NULL);
18451  if (list_member_oid(attachrel_children, RelationGetRelid(rel)))
18452  ereport(ERROR,
18453  (errcode(ERRCODE_DUPLICATE_TABLE),
18454  errmsg("circular inheritance not allowed"),
18455  errdetail("\"%s\" is already a child of \"%s\".",
18457  RelationGetRelationName(attachrel))));
18458 
18459  /* If the parent is permanent, so must be all of its partitions. */
18460  if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
18461  attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
18462  ereport(ERROR,
18463  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18464  errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
18465  RelationGetRelationName(rel))));
18466 
18467  /* Temp parent cannot have a partition that is itself not a temp */
18468  if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18469  attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
18470  ereport(ERROR,
18471  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18472  errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
18473  RelationGetRelationName(rel))));
18474 
18475  /* If the parent is temp, it must belong to this session */
18476  if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18477  !rel->rd_islocaltemp)
18478  ereport(ERROR,
18479  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18480  errmsg("cannot attach as partition of temporary relation of another session")));
18481 
18482  /* Ditto for the partition */
18483  if (attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
18484  !attachrel->rd_islocaltemp)
18485  ereport(ERROR,
18486  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18487  errmsg("cannot attach temporary relation of another session as partition")));
18488 
18489  /*
18490  * Check if attachrel has any identity columns or any columns that aren't
18491  * in the parent.
18492  */
18493  tupleDesc = RelationGetDescr(attachrel);
18494  natts = tupleDesc->natts;
18495  for (attno = 1; attno <= natts; attno++)
18496  {
18497  Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
18498  char *attributeName = NameStr(attribute->attname);
18499 
18500  /* Ignore dropped */
18501  if (attribute->attisdropped)
18502  continue;
18503 
18504  if (attribute->attidentity)
18505  ereport(ERROR,
18506  errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
18507  errmsg("table \"%s\" being attached contains an identity column \"%s\"",
18508  RelationGetRelationName(attachrel), attributeName),
18509  errdetail("The new partition may not contain an identity column."));
18510 
18511  /* Try to find the column in parent (matching on column name) */
18512  if (!SearchSysCacheExists2(ATTNAME,
18514  CStringGetDatum(attributeName)))
18515  ereport(ERROR,
18516  (errcode(ERRCODE_DATATYPE_MISMATCH),
18517  errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
18518  RelationGetRelationName(attachrel), attributeName,
18520  errdetail("The new partition may contain only the columns present in parent.")));
18521  }
18522 
18523  /*
18524  * If child_rel has row-level triggers with transition tables, we
18525  * currently don't allow it to become a partition. See also prohibitions
18526  * in ATExecAddInherit() and CreateTrigger().
18527  */
18528  trigger_name = FindTriggerIncompatibleWithInheritance(attachrel->trigdesc);
18529  if (trigger_name != NULL)
18530  ereport(ERROR,
18531  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
18532  errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
18533  trigger_name, RelationGetRelationName(attachrel)),
18534  errdetail("ROW triggers with transition tables are not supported on partitions.")));
18535 
18536  /*
18537  * Check that the new partition's bound is valid and does not overlap any
18538  * of existing partitions of the parent - note that it does not return on
18539  * error.
18540  */
18542  cmd->bound, pstate);
18543 
18544  /* Attach a new partition to the partitioned table. */
18545  attachPartitionTable(wqueue, rel, attachrel, cmd->bound);
18546 
18547  /*
18548  * Generate partition constraint from the partition bound specification.
18549  * If the parent itself is a partition, make sure to include its
18550  * constraint as well.
18551  */
18552  partBoundConstraint = get_qual_from_partbound(rel, cmd->bound);
18553  partConstraint = list_concat(partBoundConstraint,
18555 
18556  /* Skip validation if there are no constraints to validate. */
18557  if (partConstraint)
18558  {
18559  /*
18560  * Run the partition quals through const-simplification similar to
18561  * check constraints. We skip canonicalize_qual, though, because
18562  * partition quals should be in canonical form already.
18563  */
18564  partConstraint =
18565  (List *) eval_const_expressions(NULL,
18566  (Node *) partConstraint);
18567 
18568  /* XXX this sure looks wrong */
18569  partConstraint = list_make1(make_ands_explicit(partConstraint));
18570 
18571  /*
18572  * Adjust the generated constraint to match this partition's attribute
18573  * numbers.
18574  */
18575  partConstraint = map_partition_varattnos(partConstraint, 1, attachrel,
18576  rel);
18577 
18578  /* Validate partition constraints against the table being attached. */
18579  QueuePartitionConstraintValidation(wqueue, attachrel, partConstraint,
18580  false);
18581  }
18582 
18583  /*
18584  * If we're attaching a partition other than the default partition and a
18585  * default one exists, then that partition's partition constraint changes,
18586  * so add an entry to the work queue to validate it, too. (We must not do
18587  * this when the partition being attached is the default one; we already
18588  * did it above!)
18589  */
18590  if (OidIsValid(defaultPartOid))
18591  {
18592  Relation defaultrel;
18593  List *defPartConstraint;
18594 
18595  Assert(!cmd->bound->is_default);
18596 
18597  /* we already hold a lock on the default partition */
18598  defaultrel = table_open(defaultPartOid, NoLock);
18599  defPartConstraint =
18600  get_proposed_default_constraint(partBoundConstraint);
18601 
18602  /*
18603  * Map the Vars in the constraint expression from rel's attnos to
18604  * defaultrel's.
18605  */
18606  defPartConstraint =
18607  map_partition_varattnos(defPartConstraint,
18608  1, defaultrel, rel);
18609  QueuePartitionConstraintValidation(wqueue, defaultrel,
18610  defPartConstraint, true);
18611 
18612  /* keep our lock until commit. */
18613  table_close(defaultrel, NoLock);
18614  }
18615 
18616  ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachrel));
18617 
18618  /*
18619  * If the partition we just attached is partitioned itself, invalidate
18620  * relcache for all descendent partitions too to ensure that their
18621  * rd_partcheck expression trees are rebuilt; partitions already locked at
18622  * the beginning of this function.
18623  */
18624  if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18625  {
18626  ListCell *l;
18627 
18628  foreach(l, attachrel_children)
18629  {
18631  }
18632  }
18633 
18634  /* keep our lock until commit */
18635  table_close(attachrel, NoLock);
18636 
18637  return address;
18638 }
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:487
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:18321
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
Definition: tablecmds.c:18244

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

19610 {
19611  Relation partIdx;
19612  Relation partTbl;
19613  Relation parentTbl;
19614  ObjectAddress address;
19615  Oid partIdxId;
19616  Oid currParent;
19618 
19619  /*
19620  * We need to obtain lock on the index 'name' to modify it, but we also
19621  * need to read its owning table's tuple descriptor -- so we need to lock
19622  * both. To avoid deadlocks, obtain lock on the table before doing so on
19623  * the index. Furthermore, we need to examine the parent table of the
19624  * partition, so lock that one too.
19625  */
19626  state.partitionOid = InvalidOid;
19627  state.parentTblOid = parentIdx->rd_index->indrelid;
19628  state.lockedParentTbl = false;
19629  partIdxId =
19632  (void *) &state);
19633  /* Not there? */
19634  if (!OidIsValid(partIdxId))
19635  ereport(ERROR,
19636  (errcode(ERRCODE_UNDEFINED_OBJECT),
19637  errmsg("index \"%s\" does not exist", name->relname)));
19638 
19639  /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
19640  partIdx = relation_open(partIdxId, AccessExclusiveLock);
19641 
19642  /* we already hold locks on both tables, so this is safe: */
19643  parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
19644  partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
19645 
19646  ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partIdx));
19647 
19648  /* Silently do nothing if already in the right state */
19649  currParent = partIdx->rd_rel->relispartition ?
19650  get_partition_parent(partIdxId, false) : InvalidOid;
19651  if (currParent != RelationGetRelid(parentIdx))
19652  {
19653  IndexInfo *childInfo;
19654  IndexInfo *parentInfo;
19655  AttrMap *attmap;
19656  bool found;
19657  int i;
19658  PartitionDesc partDesc;
19659  Oid constraintOid,
19660  cldConstrId = InvalidOid;
19661 
19662  /*
19663  * If this partition already has an index attached, refuse the
19664  * operation.
19665  */
19666  refuseDupeIndexAttach(parentIdx, partIdx, partTbl);
19667 
19668  if (OidIsValid(currParent))
19669  ereport(ERROR,
19670  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19671  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19672  RelationGetRelationName(partIdx),
19673  RelationGetRelationName(parentIdx)),
19674  errdetail("Index \"%s\" is already attached to another index.",
19675  RelationGetRelationName(partIdx))));
19676 
19677  /* Make sure it indexes a partition of the other index's table */
19678  partDesc = RelationGetPartitionDesc(parentTbl, true);
19679  found = false;
19680  for (i = 0; i < partDesc->nparts; i++)
19681  {
19682  if (partDesc->oids[i] == state.partitionOid)
19683  {
19684  found = true;
19685  break;
19686  }
19687  }
19688  if (!found)
19689  ereport(ERROR,
19690  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19691  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19692  RelationGetRelationName(partIdx),
19693  RelationGetRelationName(parentIdx)),
19694  errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
19695  RelationGetRelationName(partIdx),
19696  RelationGetRelationName(parentTbl))));
19697 
19698  /* Ensure the indexes are compatible */
19699  childInfo = BuildIndexInfo(partIdx);
19700  parentInfo = BuildIndexInfo(parentIdx);
19701  attmap = build_attrmap_by_name(RelationGetDescr(partTbl),
19702  RelationGetDescr(parentTbl),
19703  false);
19704  if (!CompareIndexInfo(childInfo, parentInfo,
19705  partIdx->rd_indcollation,
19706  parentIdx->rd_indcollation,
19707  partIdx->rd_opfamily,
19708  parentIdx->rd_opfamily,
19709  attmap))
19710  ereport(ERROR,
19711  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19712  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19713  RelationGetRelationName(partIdx),
19714  RelationGetRelationName(parentIdx)),
19715  errdetail("The index definitions do not match.")));
19716 
19717  /*
19718  * If there is a constraint in the parent, make sure there is one in
19719  * the child too.
19720  */
19721  constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(parentTbl),
19722  RelationGetRelid(parentIdx));
19723 
19724  if (OidIsValid(constraintOid))
19725  {
19726  cldConstrId = get_relation_idx_constraint_oid(RelationGetRelid(partTbl),
19727  partIdxId);
19728  if (!OidIsValid(cldConstrId))
19729  ereport(ERROR,
19730  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19731  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19732  RelationGetRelationName(partIdx),
19733  RelationGetRelationName(parentIdx)),
19734  errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
19735  RelationGetRelationName(parentIdx),
19736  RelationGetRelationName(parentTbl),
19737  RelationGetRelationName(partIdx))));
19738  }
19739 
19740  /* All good -- do it */
19741  IndexSetParentIndex(partIdx, RelationGetRelid(parentIdx));
19742  if (OidIsValid(constraintOid))
19743  ConstraintSetParentConstraint(cldConstrId, constraintOid,
19744  RelationGetRelid(partTbl));
19745 
19746  free_attrmap(attmap);
19747 
19748  validatePartitionedIndex(parentIdx, parentTbl);
19749  }
19750 
19751  relation_close(parentTbl, AccessShareLock);
19752  /* keep these locks till commit */
19753  relation_close(partTbl, NoLock);
19754  relation_close(partIdx, NoLock);
19755 
19756  return address;
19757 }
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:2511
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition: indexcmds.c:4280
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition: partition.c:53
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
Form_pg_index rd_index
Definition: rel.h:192
Oid * rd_opfamily
Definition: rel.h:207
Oid * rd_indcollation
Definition: rel.h:217
Definition: regguts.h:323
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:19555
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
Definition: tablecmds.c:19787
static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
Definition: tablecmds.c:19764
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, and validatePartitionedIndex().

Referenced by ATExecCmd().

◆ ATExecChangeOwner()

void ATExecChangeOwner ( Oid  relationOid,
Oid  newOwnerId,
bool  recursing,
LOCKMODE  lockmode 
)

Definition at line 14314 of file tablecmds.c.

14315 {
14316  Relation target_rel;
14317  Relation class_rel;
14318  HeapTuple tuple;
14319  Form_pg_class tuple_class;
14320 
14321  /*
14322  * Get exclusive lock till end of transaction on the target table. Use
14323  * relation_open so that we can work on indexes and sequences.
14324  */
14325  target_rel = relation_open(relationOid, lockmode);
14326 
14327  /* Get its pg_class tuple, too */
14328  class_rel = table_open(RelationRelationId, RowExclusiveLock);
14329 
14330  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
14331  if (!HeapTupleIsValid(tuple))
14332  elog(ERROR, "cache lookup failed for relation %u", relationOid);
14333  tuple_class = (Form_pg_class) GETSTRUCT(tuple);
14334 
14335  /* Can we change the ownership of this tuple? */
14336  switch (tuple_class->relkind)
14337  {
14338  case RELKIND_RELATION:
14339  case RELKIND_VIEW:
14340  case RELKIND_MATVIEW:
14341  case RELKIND_FOREIGN_TABLE:
14342  case RELKIND_PARTITIONED_TABLE:
14343  /* ok to change owner */
14344  break;
14345  case RELKIND_INDEX:
14346  if (!recursing)
14347  {
14348  /*
14349  * Because ALTER INDEX OWNER used to be allowed, and in fact
14350  * is generated by old versions of pg_dump, we give a warning
14351  * and do nothing rather than erroring out. Also, to avoid
14352  * unnecessary chatter while restoring those old dumps, say
14353  * nothing at all if the command would be a no-op anyway.
14354  */
14355  if (tuple_class->relowner != newOwnerId)
14356  ereport(WARNING,
14357  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14358  errmsg("cannot change owner of index \"%s\"",
14359  NameStr(tuple_class->relname)),
14360  errhint("Change the ownership of the index's table instead.")));
14361  /* quick hack to exit via the no-op path */
14362  newOwnerId = tuple_class->relowner;
14363  }
14364  break;
14365  case RELKIND_PARTITIONED_INDEX:
14366  if (recursing)
14367  break;
14368  ereport(ERROR,
14369  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14370  errmsg("cannot change owner of index \"%s\"",
14371  NameStr(tuple_class->relname)),
14372  errhint("Change the ownership of the index's table instead.")));
14373  break;
14374  case RELKIND_SEQUENCE:
14375  if (!recursing &&
14376  tuple_class->relowner != newOwnerId)
14377  {
14378  /* if it's an owned sequence, disallow changing it by itself */
14379  Oid tableId;
14380  int32 colId;
14381 
14382  if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
14383  sequenceIsOwned(relationOid, DEPENDENCY_INTERNAL, &tableId, &colId))
14384  ereport(ERROR,
14385  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14386  errmsg("cannot change owner of sequence \"%s\"",
14387  NameStr(tuple_class->relname)),
14388  errdetail("Sequence \"%s\" is linked to table \"%s\".",
14389  NameStr(tuple_class->relname),
14390  get_rel_name(tableId))));
14391  }
14392  break;
14393  case RELKIND_COMPOSITE_TYPE:
14394  if (recursing)
14395  break;
14396  ereport(ERROR,
14397  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14398  errmsg("\"%s\" is a composite type",
14399  NameStr(tuple_class->relname)),
14400  /* translator: %s is an SQL ALTER command */
14401  errhint("Use %s instead.",
14402  "ALTER TYPE")));
14403  break;
14404  case RELKIND_TOASTVALUE:
14405  if (recursing)
14406  break;
14407  /* FALL THRU */
14408  default:
14409  ereport(ERROR,
14410  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14411  errmsg("cannot change owner of relation \"%s\"",
14412  NameStr(tuple_class->relname)),
14413  errdetail_relkind_not_supported(tuple_class->relkind)));
14414  }
14415 
14416  /*
14417  * If the new owner is the same as the existing owner, consider the
14418  * command to have succeeded. This is for dump restoration purposes.
14419  */
14420  if (tuple_class->relowner != newOwnerId)
14421  {
14422  Datum repl_val[Natts_pg_class];
14423  bool repl_null[Natts_pg_class];
14424  bool repl_repl[Natts_pg_class];
14425  Acl *newAcl;
14426  Datum aclDatum;
14427  bool isNull;
14428  HeapTuple newtuple;
14429 
14430  /* skip permission checks when recursing to index or toast table */
14431  if (!recursing)
14432  {
14433  /* Superusers can always do it */
14434  if (!superuser())
14435  {
14436  Oid namespaceOid = tuple_class->relnamespace;
14437  AclResult aclresult;
14438 
14439  /* Otherwise, must be owner of the existing object */
14440  if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
14442  RelationGetRelationName(target_rel));
14443 
14444  /* Must be able to become new owner */
14445  check_can_set_role(GetUserId(), newOwnerId);
14446 
14447  /* New owner must have CREATE privilege on namespace */
14448  aclresult = object_aclcheck(NamespaceRelationId, namespaceOid, newOwnerId,
14449  ACL_CREATE);
14450  if (aclresult != ACLCHECK_OK)
14451  aclcheck_error(aclresult, OBJECT_SCHEMA,
14452  get_namespace_name(namespaceOid));
14453  }
14454  }
14455 
14456  memset(repl_null, false, sizeof(repl_null));
14457  memset(repl_repl, false, sizeof(repl_repl));
14458 
14459  repl_repl[Anum_pg_class_relowner - 1] = true;
14460  repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId);
14461 
14462  /*
14463  * Determine the modified ACL for the new owner. This is only
14464  * necessary when the ACL is non-null.
14465  */
14466  aclDatum = SysCacheGetAttr(RELOID, tuple,
14467  Anum_pg_class_relacl,
14468  &isNull);
14469  if (!isNull)
14470  {
14471  newAcl = aclnewowner(DatumGetAclP(aclDatum),
14472  tuple_class->relowner, newOwnerId);
14473  repl_repl[Anum_pg_class_relacl - 1] = true;
14474  repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl);
14475  }
14476 
14477  newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl);
14478 
14479  CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
14480 
14481  heap_freetuple(newtuple);
14482 
14483  /*
14484  * We must similarly update any per-column ACLs to reflect the new
14485  * owner; for neatness reasons that's split out as a subroutine.
14486  */
14487  change_owner_fix_column_acls(relationOid,
14488  tuple_class->relowner,
14489  newOwnerId);
14490 
14491  /*
14492  * Update owner dependency reference, if any. A composite type has
14493  * none, because it's tracked for the pg_type entry instead of here;
14494  * indexes and TOAST tables don't have their own entries either.
14495  */
14496  if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
14497  tuple_class->relkind != RELKIND_INDEX &&
14498  tuple_class->relkind != RELKIND_PARTITIONED_INDEX &&
14499  tuple_class->relkind != RELKIND_TOASTVALUE)
14500  changeDependencyOnOwner(RelationRelationId, relationOid,
14501  newOwnerId);
14502 
14503  /*
14504  * Also change the ownership of the table's row type, if it has one
14505  */
14506  if (OidIsValid(tuple_class->reltype))
14507  AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
14508 
14509  /*
14510  * If we are operating on a table or materialized view, also change
14511  * the ownership of any indexes and sequences that belong to the
14512  * relation, as well as its toast table (if it has one).
14513  */
14514  if (tuple_class->relkind == RELKIND_RELATION ||
14515  tuple_class->relkind == RELKIND_PARTITIONED_TABLE ||
14516  tuple_class->relkind == RELKIND_MATVIEW ||
14517  tuple_class->relkind == RELKIND_TOASTVALUE)
14518  {
14519  List *index_oid_list;
14520  ListCell *i;
14521 
14522  /* Find all the indexes belonging to this relation */
14523  index_oid_list = RelationGetIndexList(target_rel);
14524 
14525  /* For each index, recursively change its ownership */
14526  foreach(i, index_oid_list)
14527  ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
14528 
14529  list_free(index_oid_list);
14530  }
14531 
14532  /* If it has a toast table, recurse to change its ownership */
14533  if (tuple_class->reltoastrelid != InvalidOid)
14534  ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId,
14535  true, lockmode);
14536 
14537  /* If it has dependent sequences, recurse to change them too */
14538  change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
14539  }
14540 
14541  InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0);
14542 
14543  ReleaseSysCache(tuple);
14544  table_close(class_rel, RowExclusiveLock);
14545  relation_close(target_rel, NoLock);
14546 }
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition: acl.c:1102
void check_can_set_role(Oid member, Oid role)
Definition: acl.c:5191
#define DatumGetAclP(X)
Definition: acl.h:120
#define WARNING
Definition: elog.h:36
@ OBJECT_SCHEMA
Definition: parsenodes.h:2297
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
Definition: pg_shdepend.c:316
bool superuser(void)
Definition: superuser.c:46
void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:14314
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
Definition: tablecmds.c:14620
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
Definition: tablecmds.c:14555
void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
Definition: typecmds.c:3987

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

◆ ATExecCheckNotNull()

static void ATExecCheckNotNull ( AlteredTableInfo tab,
Relation  rel,
const char *  colName,
LOCKMODE  lockmode 
)
static

Definition at line 7817 of file tablecmds.c.

7819 {
7820  HeapTuple tuple;
7821 
7822  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
7823 
7824  if (!HeapTupleIsValid(tuple))
7825  ereport(ERROR,
7826  errcode(ERRCODE_UNDEFINED_COLUMN),
7827  errmsg("column \"%s\" of relation \"%s\" does not exist",
7828  colName, RelationGetRelationName(rel)));
7829 
7830  if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
7831  ereport(ERROR,
7832  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7833  errmsg("constraint must be added to child tables too"),
7834  errdetail("Column \"%s\" of relation \"%s\" is not already NOT NULL.",
7835  colName, RelationGetRelationName(rel)),
7836  errhint("Do not specify the ONLY keyword.")));
7837 
7838  ReleaseSysCache(tuple);
7839 }
bool attnotnull
Definition: pg_attribute.h:130

References attnotnull, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, GETSTRUCT, HeapTupleIsValid, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), and SearchSysCacheAttName().

Referenced by ATExecCmd().

◆ ATExecClusterOn()

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

Definition at line 14689 of file tablecmds.c.

14690 {
14691  Oid indexOid;
14692  ObjectAddress address;
14693 
14694  indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
14695 
14696  if (!OidIsValid(indexOid))
14697  ereport(ERROR,
14698  (errcode(ERRCODE_UNDEFINED_OBJECT),
14699  errmsg("index \"%s\" for table \"%s\" does not exist",
14700  indexName, RelationGetRelationName(rel))));
14701 
14702  /* Check index is valid to cluster on */
14703  check_index_is_clusterable(rel, indexOid, lockmode);
14704 
14705  /* And do the work */
14706  mark_index_clustered(rel, indexOid, false);
14707 
14708  ObjectAddressSet(address,
14709  RelationRelationId, indexOid);
14710 
14711  return address;
14712 }
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 5213 of file tablecmds.c.

5216 {
5218  Relation rel = tab->rel;
5219 
5220  switch (cmd->subtype)
5221  {
5222  case AT_AddColumn: /* ADD COLUMN */
5223  case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5224  address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5225  cmd->recurse, false,
5226  lockmode, cur_pass, context);
5227  break;
5228  case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5229  address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5230  break;
5231  case AT_CookedColumnDefault: /* add a pre-cooked default */
5232  address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5233  break;
5234  case AT_AddIdentity:
5235  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5236  cur_pass, context);
5237  Assert(cmd != NULL);
5238  address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5239  break;
5240  case AT_SetIdentity:
5241  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5242  cur_pass, context);
5243  Assert(cmd != NULL);
5244  address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5245  break;
5246  case AT_DropIdentity:
5247  address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5248  break;
5249  case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5250  address = ATExecDropNotNull(rel, cmd->name, lockmode);
5251  break;
5252  case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5253  address = ATExecSetNotNull(tab, rel, cmd->name, lockmode);
5254  break;
5255  case AT_CheckNotNull: /* check column is already marked NOT NULL */
5256  ATExecCheckNotNull(tab, rel, cmd->name, lockmode);
5257  break;
5258  case AT_SetExpression:
5259  address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5260  break;
5261  case AT_DropExpression:
5262  address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5263  break;
5264  case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5265  address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5266  break;
5267  case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5268  address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5269  break;
5270  case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5271  address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5272  break;
5273  case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5274  address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5275  break;
5276  case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5277  address = ATExecSetCompression(rel, cmd->name, cmd->def,
5278  lockmode);
5279  break;
5280  case AT_DropColumn: /* DROP COLUMN */
5281  address = ATExecDropColumn(wqueue, rel, cmd->name,
5282  cmd->behavior, cmd->recurse, false,
5283  cmd->missing_ok, lockmode,
5284  NULL);
5285  break;
5286  case AT_AddIndex: /* ADD INDEX */
5287  address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5288  lockmode);
5289  break;
5290  case AT_ReAddIndex: /* ADD INDEX */
5291  address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5292  lockmode);
5293  break;
5294  case AT_ReAddStatistics: /* ADD STATISTICS */
5295  address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5296  true, lockmode);
5297  break;
5298  case AT_AddConstraint: /* ADD CONSTRAINT */
5299  /* Transform the command only during initial examination */
5300  if (cur_pass == AT_PASS_ADD_CONSTR)
5301  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5302  cmd->recurse, lockmode,
5303  cur_pass, context);
5304  /* Depending on constraint type, might be no more work to do now */
5305  if (cmd != NULL)
5306  address =
5307  ATExecAddConstraint(wqueue, tab, rel,
5308  (Constraint *) cmd->def,
5309  cmd->recurse, false, lockmode);
5310  break;
5311  case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5312  address =
5313  ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5314  true, true, lockmode);
5315  break;
5316  case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5317  * constraint */
5318  address =
5319  AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5320  ((AlterDomainStmt *) cmd->def)->def,
5321  NULL);
5322  break;
5323  case AT_ReAddComment: /* Re-add existing comment */
5324  address = CommentObject((CommentStmt *) cmd->def);
5325  break;
5326  case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5327  address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5328  lockmode);
5329  break;
5330  case AT_AlterConstraint: /* ALTER CONSTRAINT */
5331  address = ATExecAlterConstraint(rel, cmd, false, false, lockmode);
5332  break;
5333  case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5334  address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5335  false, lockmode);
5336  break;
5337  case AT_DropConstraint: /* DROP CONSTRAINT */
5338  ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5339  cmd->recurse, false,
5340  cmd->missing_ok, lockmode);
5341  break;
5342  case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5343  /* parse transformation was done earlier */
5344  address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5345  break;
5346  case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5347  address =
5349  (List *) cmd->def, lockmode);
5350  break;
5351  case AT_ChangeOwner: /* ALTER OWNER */
5353  get_rolespec_oid(cmd->newowner, false),
5354  false, lockmode);
5355  break;
5356  case AT_ClusterOn: /* CLUSTER ON */
5357  address = ATExecClusterOn(rel, cmd->name, lockmode);
5358  break;
5359  case AT_DropCluster: /* SET WITHOUT CLUSTER */
5360  ATExecDropCluster(rel, lockmode);
5361  break;
5362  case AT_SetLogged: /* SET LOGGED */
5363  case AT_SetUnLogged: /* SET UNLOGGED */
5364  break;
5365  case AT_DropOids: /* SET WITHOUT OIDS */
5366  /* nothing to do here, oid columns don't exist anymore */
5367  break;
5368  case AT_SetAccessMethod: /* SET ACCESS METHOD */
5369 
5370  /*
5371  * Only do this for partitioned tables, for which this is just a
5372  * catalog change. Tables with storage are handled by Phase 3.
5373  */
5374  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5375  tab->chgAccessMethod)
5377  break;
5378  case AT_SetTableSpace: /* SET TABLESPACE */
5379 
5380  /*
5381  * Only do this for partitioned tables and indexes, for which this
5382  * is just a catalog change. Other relation types which have
5383  * storage are handled by Phase 3.
5384  */
5385  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5386  rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5388 
5389  break;
5390  case AT_SetRelOptions: /* SET (...) */
5391  case AT_ResetRelOptions: /* RESET (...) */
5392  case AT_ReplaceRelOptions: /* replace entire option list */
5393  ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5394  break;
5395  case AT_EnableTrig: /* ENABLE TRIGGER name */
5396  ATExecEnableDisableTrigger(rel, cmd->name,
5397  TRIGGER_FIRES_ON_ORIGIN, false,
5398  cmd->recurse,
5399  lockmode);
5400  break;
5401  case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5402  ATExecEnableDisableTrigger(rel, cmd->name,
5403  TRIGGER_FIRES_ALWAYS, false,
5404  cmd->recurse,
5405  lockmode);
5406  break;
5407  case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5408  ATExecEnableDisableTrigger(rel, cmd->name,
5409  TRIGGER_FIRES_ON_REPLICA, false,
5410  cmd->recurse,
5411  lockmode);
5412  break;
5413  case AT_DisableTrig: /* DISABLE TRIGGER name */
5414  ATExecEnableDisableTrigger(rel, cmd->name,
5415  TRIGGER_DISABLED, false,
5416  cmd->recurse,
5417  lockmode);
5418  break;
5419  case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5420  ATExecEnableDisableTrigger(rel, NULL,
5421  TRIGGER_FIRES_ON_ORIGIN, false,
5422  cmd->recurse,
5423  lockmode);
5424  break;
5425  case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5426  ATExecEnableDisableTrigger(rel, NULL,
5427  TRIGGER_DISABLED, false,
5428  cmd->recurse,
5429  lockmode);
5430  break;
5431  case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5432  ATExecEnableDisableTrigger(rel, NULL,
5434  cmd->recurse,
5435  lockmode);
5436  break;
5437  case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5438  ATExecEnableDisableTrigger(rel, NULL,
5439  TRIGGER_DISABLED, true,
5440  cmd->recurse,
5441  lockmode);
5442  break;
5443 
5444  case AT_EnableRule: /* ENABLE RULE name */
5445  ATExecEnableDisableRule(rel, cmd->name,
5446  RULE_FIRES_ON_ORIGIN, lockmode);
5447  break;
5448  case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5449  ATExecEnableDisableRule(rel, cmd->name,
5450  RULE_FIRES_ALWAYS, lockmode);
5451  break;
5452  case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5453  ATExecEnableDisableRule(rel, cmd->name,
5454  RULE_FIRES_ON_REPLICA, lockmode);
5455  break;
5456  case AT_DisableRule: /* DISABLE RULE name */
5457  ATExecEnableDisableRule(rel, cmd->name,
5458  RULE_DISABLED, lockmode);
5459  break;
5460 
5461  case AT_AddInherit:
5462  address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5463  break;
5464  case AT_DropInherit:
5465  address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5466  break;
5467  case AT_AddOf:
5468  address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5469  break;
5470  case AT_DropOf:
5471  ATExecDropOf(rel, lockmode);
5472  break;
5473  case AT_ReplicaIdentity:
5474  ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5475  break;
5476  case AT_EnableRowSecurity:
5477  ATExecSetRowSecurity(rel, true);
5478  break;
5479  case AT_DisableRowSecurity:
5480  ATExecSetRowSecurity(rel, false);
5481  break;
5482  case AT_ForceRowSecurity:
5483  ATExecForceNoForceRowSecurity(rel, true);
5484  break;
5485  case AT_NoForceRowSecurity:
5486  ATExecForceNoForceRowSecurity(rel, false);
5487  break;
5488  case AT_GenericOptions:
5489  ATExecGenericOptions(rel, (List *) cmd->def);
5490  break;
5491  case AT_AttachPartition:
5492  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5493  cur_pass, context);
5494  Assert(cmd != NULL);
5495  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5496  address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5497  context);
5498  else
5499  address = ATExecAttachPartitionIdx(wqueue, rel,
5500  ((PartitionCmd *) cmd->def)->name);
5501  break;
5502  case AT_DetachPartition:
5503  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5504  cur_pass, context);
5505  Assert(cmd != NULL);
5506  /* ATPrepCmd ensures it must be a table */
5507  Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5508  address = ATExecDetachPartition(wqueue, tab, rel,
5509  ((PartitionCmd *) cmd->def)->name,
5510  ((PartitionCmd *) cmd->def)->concurrent);
5511  break;
5513  address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5514  break;
5515  case AT_SplitPartition:
5516  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5517  cur_pass, context);
5518  Assert(cmd != NULL);
5519  Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5520  ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5521  context);
5522  break;
5523  case AT_MergePartitions:
5524  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5525  cur_pass, context);
5526  Assert(cmd != NULL);
5527  Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5528  ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5529  context);
5530  break;
5531  default: /* oops */
5532  elog(ERROR, "unrecognized alter table type: %d",
5533  (int) cmd->subtype);
5534  break;
5535  }
5536 
5537  /*
5538  * Report the subcommand to interested event triggers.
5539  */
5540  if (cmd)
5541  EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5542 
5543  /*
5544  * Bump the command counter to ensure the next subcommand in the sequence
5545  * can see the changes so far
5546  */
5548 }
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition: acl.c:5454
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:2439
DropBehavior behavior
Definition: parsenodes.h:2442
bool chgAccessMethod
Definition: tablecmds.c:190
Relation rel
Definition: tablecmds.c:180
static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, const char *colName, LOCKMODE lockmode)
Definition: tablecmds.c:7735
static ObjectAddress ATExecAlterColumnGenericOptions(Relation rel, const char *colName, List *options, LOCKMODE lockmode)
Definition: tablecmds.c:14197
static ObjectAddress ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:15498
static void ATExecDropOf(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:16465
static ObjectAddress ATExecDropExpression(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:8494
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition: tablecmds.c:7883
static void ATExecDropConstraint(Relation rel, const char *constrName, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)
Definition: tablecmds.c:12428
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8221
static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode)
Definition: tablecmds.c:7531
static ObjectAddress ATExecValidateConstraint(List **wqueue, Relation rel, char *constrName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:11576
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition: tablecmds.c:9330
static ObjectAddress ATExecDetachPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
Definition: tablecmds.c:18987
static void ATExecForceNoForceRowSecurity(Relation rel, bool force_rls)
Definition: tablecmds.c:16735
static void ATExecEnableDisableTrigger(Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition: tablecmds.c:15441
static ObjectAddress ATExecSetOptions(Relation rel, const char *colName, Node *options, bool isReset, LOCKMODE lockmode)
Definition: tablecmds.c:8720
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:8953
static void ATExecSetRowSecurity(Relation rel, bool rls)
Definition: tablecmds.c:16705
static void ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel, const char *colName, LOCKMODE lockmode)
Definition: tablecmds.c:7817
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition: tablecmds.c:7969
static void ATExecMergePartitions(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:20644
static void ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
Definition: tablecmds.c:14887
static ObjectAddress ATExecSetIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition: tablecmds.c:8104
static ObjectAddress ATExecAlterConstraint(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:11287
static void ATExecGenericOptions(Relation rel, List *options)
Definition: tablecmds.c:16764
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8585
static ObjectAddress ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode)
Definition: tablecmds.c:13007
static void ATExecDropCluster(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:14721
static ObjectAddress ATExecSetExpression(AlteredTableInfo *tab, Relation rel, const char *colName, Node *newExpr, LOCKMODE lockmode)
Definition: tablecmds.c:8335
static ObjectAddress ATExecAddOf(Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
Definition: tablecmds.c:16323
static ObjectAddress ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
Definition: tablecmds.c:15978
static ObjectAddress ATExecAddIndexConstraint(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:9238
static void ATExecReplicaIdentity(Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
Definition: tablecmds.c:16597
static ObjectAddress ATExecDetachPartitionFinalize(Relation rel, RangeVar *name)
Definition: tablecmds.c:19406
static ObjectAddress ATExecSetStorage(Relation rel, const char *colName, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:8862
static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:18348
static void ATExecSetAccessMethodNoStorage(Relation rel, Oid newAccessMethodId)
Definition: tablecmds.c:14767
static ObjectAddress ATExecAddIndex(AlteredTableInfo *tab, Relation rel, IndexStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9154
static ObjectAddress ATExecSetCompression(Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
Definition: tablecmds.c:16845
static void ATExecSetTableSpaceNoStorage(Relation rel, Oid newTableSpace)
Definition: tablecmds.c:15183
static ObjectAddress ATExecAttachPartitionIdx(List **wqueue, Relation parentIdx, RangeVar *name)
Definition: tablecmds.c:19609
static void ATExecSplitPartition(List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
Definition: tablecmds.c:20419
static ObjectAddress ATExecAddStatistics(AlteredTableInfo *tab, Relation rel, CreateStatsStmt *stmt, bool is_rebuild, LOCKMODE lockmode)
Definition: tablecmds.c:9217
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
Definition: tablecmds.c:14689
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
Definition: tablecmds.c:15459
#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_CheckNotNull, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_MergePartitions, AT_NoForceRowSecurity, AT_PASS_ADD_CONSTR, AT_ReAddComment, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReAddIndex, AT_ReAddStatistics, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, AT_SplitPartition, AT_ValidateConstraint, ATExecAddColumn(), ATExecAddConstraint(), ATExecAddIdentity(), ATExecAddIndex(), ATExecAddIndexConstraint(), ATExecAddInherit(), ATExecAddOf(), ATExecAddStatistics(), ATExecAlterColumnGenericOptions(), ATExecAlterColumnType(), ATExecAlterConstraint(), ATExecAttachPartition(), ATExecAttachPartitionIdx(), ATExecChangeOwner(), ATExecCheckNotNull(), ATExecClusterOn(), ATExecColumnDefault(), ATExecCookedColumnDefault(), ATExecDetachPartition(), ATExecDetachPartitionFinalize(), ATExecDropCluster(), ATExecDropColumn(), ATExecDropConstraint(), ATExecDropExpression(), ATExecDropIdentity(), ATExecDropInherit(), ATExecDropNotNull(), ATExecDropOf(), ATExecEnableDisableRule(), ATExecEnableDisableTrigger(), ATExecForceNoForceRowSecurity(), ATExecGenericOptions(), ATExecMergePartitions(), ATExecReplicaIdentity(), ATExecSetAccessMethodNoStorage(), ATExecSetCompression(), ATExecSetExpression(), ATExecSetIdentity(), ATExecSetNotNull(), ATExecSetOptions(), ATExecSetRelOptions(), ATExecSetRowSecurity(), ATExecSetStatistics(), ATExecSetStorage(), ATExecSetTableSpaceNoStorage(), ATExecSplitPartition(), ATExecValidateConstraint(), ATParseTransformCmd(), AlterTableCmd::behavior, 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 7883 of file tablecmds.c.

7885 {
7886  TupleDesc tupdesc = RelationGetDescr(rel);
7888  ObjectAddress address;
7889 
7890  /*
7891  * get the number of the attribute
7892  */
7893  attnum = get_attnum(RelationGetRelid(rel), colName);
7894  if (attnum == InvalidAttrNumber)
7895  ereport(ERROR,
7896  (errcode(ERRCODE_UNDEFINED_COLUMN),
7897  errmsg("column \"%s\" of relation \"%s\" does not exist",
7898  colName, RelationGetRelationName(rel))));
7899 
7900  /* Prevent them from altering a system attribute */
7901  if (attnum <= 0)
7902  ereport(ERROR,
7903  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7904  errmsg("cannot alter system column \"%s\"",
7905  colName)));
7906 
7907  if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
7908  ereport(ERROR,
7909  (errcode(ERRCODE_SYNTAX_ERROR),
7910  errmsg("column \"%s\" of relation \"%s\" is an identity column",
7911  colName, RelationGetRelationName(rel)),
7912  /* translator: %s is an SQL ALTER command */
7913  newDefault ? 0 : errhint("Use %s instead.",
7914  "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
7915 
7916  if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
7917  ereport(ERROR,
7918  (errcode(ERRCODE_SYNTAX_ERROR),
7919  errmsg("column \"%s\" of relation \"%s\" is a generated column",
7920  colName, RelationGetRelationName(rel)),
7921  newDefault ?
7922  /* translator: %s is an SQL ALTER command */
7923  errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
7924  (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
7925  errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
7926 
7927  /*
7928  * Remove any old default for the column. We use RESTRICT here for
7929  * safety, but at present we do not expect anything to depend on the
7930  * default.
7931  *
7932  * We treat removing the existing default as an internal operation when it
7933  * is preparatory to adding a new default, but as a user-initiated
7934  * operation when the user asked for a drop.
7935  */
7937  newDefault != NULL);
7938 
7939  if (newDefault)
7940  {
7941  /* SET DEFAULT */
7942  RawColumnDefault *rawEnt;
7943 
7944  rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
7945  rawEnt->attnum = attnum;
7946  rawEnt->raw_default = newDefault;
7947  rawEnt->missingMode = false;
7948  rawEnt->generated = '\0';
7949 
7950  /*
7951  * This function is intended for CREATE TABLE, so it processes a
7952  * _list_ of defaults, but we just do one.
7953  */
7955  false, true, false, NULL);
7956  }
7957 
7958  ObjectAddressSubSet(address, RelationRelationId,
7959  RelationGetRelid(rel), attnum);
7960  return address;
7961 }
#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 7969 of file tablecmds.c.

7971 {
7972  ObjectAddress address;
7973 
7974  /* We assume no checking is required */
7975 
7976  /*
7977  * Remove any old default for the column. We use RESTRICT here for
7978  * safety, but at present we do not expect anything to depend on the
7979  * default. (In ordinary cases, there could not be a default in place
7980  * anyway, but it's possible when combining LIKE with inheritance.)
7981  */
7983  true);
7984 
7985  (void) StoreAttrDefault(rel, attnum, newDefault, true, false);
7986 
7987  ObjectAddressSubSet(address, RelationRelationId,
7988  RelationGetRelid(rel), attnum);
7989  return address;
7990 }

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

18989 {
18990  Relation partRel;
18991  ObjectAddress address;
18992  Oid defaultPartOid;
18993 
18994  /*
18995  * We must lock the default partition, because detaching this partition
18996  * will change its partition constraint.
18997  */
18998  defaultPartOid =
19000  if (OidIsValid(defaultPartOid))
19001  {
19002  /*
19003  * Concurrent detaching when a default partition exists is not
19004  * supported. The main problem is that the default partition
19005  * constraint would change. And there's a definitional problem: what
19006  * should happen to the tuples that are being inserted that belong to
19007  * the partition being detached? Putting them on the partition being
19008  * detached would be wrong, since they'd become "lost" after the
19009  * detaching completes but we cannot put them in the default partition
19010  * either until we alter its partition constraint.
19011  *
19012  * I think we could solve this problem if we effected the constraint
19013  * change before committing the first transaction. But the lock would
19014  * have to remain AEL and it would cause concurrent query planning to
19015  * be blocked, so changing it that way would be even worse.
19016  */
19017  if (concurrent)
19018  ereport(ERROR,
19019  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19020  errmsg("cannot detach partitions concurrently when a default partition exists")));
19021  LockRelationOid(defaultPartOid, AccessExclusiveLock);
19022  }
19023 
19024  /*
19025  * In concurrent mode, the partition is locked with share-update-exclusive
19026  * in the first transaction. This allows concurrent transactions to be
19027  * doing DML to the partition.
19028  */
19029  partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
19031 
19032  /*
19033  * Check inheritance conditions and either delete the pg_inherits row (in
19034  * non-concurrent mode) or just set the inhdetachpending flag.
19035  */
19036  if (!concurrent)
19037  RemoveInheritance(partRel, rel, false);
19038  else
19039  MarkInheritDetached(partRel, rel);
19040 
19041  /*
19042  * Ensure that foreign keys still hold after this detach. This keeps
19043  * locks on the referencing tables, which prevents concurrent transactions
19044  * from adding rows that we wouldn't see. For this to work in concurrent
19045  * mode, it is critical that the partition appears as no longer attached
19046  * for the RI queries as soon as the first transaction commits.
19047  */
19049 
19050  /*
19051  * Concurrent mode has to work harder; first we add a new constraint to
19052  * the partition that matches the partition constraint. Then we close our
19053  * existing transaction, and in a new one wait for all processes to catch
19054  * up on the catalog updates we've done so far; at that point we can
19055  * complete the operation.
19056  */
19057  if (concurrent)
19058  {
19059  Oid partrelid,
19060  parentrelid;
19061  LOCKTAG tag;
19062  char *parentrelname;
19063  char *partrelname;
19064 
19065  /*
19066  * Add a new constraint to the partition being detached, which
19067  * supplants the partition constraint (unless there is one already).
19068  */
19069  DetachAddConstraintIfNeeded(wqueue, partRel);
19070 
19071  /*
19072  * We're almost done now; the only traces that remain are the
19073  * pg_inherits tuple and the partition's relpartbounds. Before we can
19074  * remove those, we need to wait until all transactions that know that
19075  * this is a partition are gone.
19076  */
19077 
19078  /*
19079  * Remember relation OIDs to re-acquire them later; and relation names
19080  * too, for error messages if something is dropped in between.
19081  */
19082  partrelid = RelationGetRelid(partRel);
19083  parentrelid = RelationGetRelid(rel);
19084  parentrelname = MemoryContextStrdup(PortalContext,
19086  partrelname = MemoryContextStrdup(PortalContext,
19087  RelationGetRelationName(partRel));
19088 
19089  /* Invalidate relcache entries for the parent -- must be before close */
19091 
19092  table_close(partRel, NoLock);
19093  table_close(rel, NoLock);
19094  tab->rel = NULL;
19095 
19096  /* Make updated catalog entry visible */
19099 
19101 
19102  /*
19103  * Now wait. This ensures that all queries that were planned
19104  * including the partition are finished before we remove the rest of
19105  * catalog entries. We don't need or indeed want to acquire this
19106  * lock, though -- that would block later queries.
19107  *
19108  * We don't need to concern ourselves with waiting for a lock on the
19109  * partition itself, since we will acquire AccessExclusiveLock below.
19110  */
19111  SET_LOCKTAG_RELATION(tag, MyDatabaseId, parentrelid);
19113 
19114  /*
19115  * Now acquire locks in both relations again. Note they may have been
19116  * removed in the meantime, so care is required.
19117  */
19118  rel = try_relation_open(parentrelid, ShareUpdateExclusiveLock);
19119  partRel = try_relation_open(partrelid, AccessExclusiveLock);
19120 
19121  /* If the relations aren't there, something bad happened; bail out */
19122  if (rel == NULL)
19123  {
19124  if (partRel != NULL) /* shouldn't happen */
19125  elog(WARNING, "dangling partition \"%s\" remains, can't fix",
19126  partrelname);
19127  ereport(ERROR,
19128  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19129  errmsg("partitioned table \"%s\" was removed concurrently",
19130  parentrelname)));
19131  }
19132  if (partRel == NULL)
19133  ereport(ERROR,
19134  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19135  errmsg("partition \"%s\" was removed concurrently", partrelname)));
19136 
19137  tab->rel = rel;
19138  }
19139 
19140  /* Do the final part of detaching */
19141  DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
19142 
19143  ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19144 
19145  /* keep our lock until commit */
19146  table_close(partRel, NoLock);
19147 
19148  return address;
19149 }
Oid MyDatabaseId
Definition: globals.c:92
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition: lmgr.c:897
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition: lock.h:181
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition: mcxt.c:1683
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:16020
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
Definition: tablecmds.c:16103
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
Definition: tablecmds.c:19158
static void DetachAddConstraintIfNeeded(List **wqueue, Relation partRel)
Definition: tablecmds.c:19441
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
Definition: tablecmds.c:19941
void StartTransactionCommand(void)
Definition: xact.c:3033
void CommitTransactionCommand(void)
Definition: xact.c:3131

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

19407 {
19408  Relation partRel;
19409  ObjectAddress address;
19410  Snapshot snap = GetActiveSnapshot();
19411 
19413 
19414  /*
19415  * Wait until existing snapshots are gone. This is important if the
19416  * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
19417  * user could immediately run DETACH FINALIZE without actually waiting for
19418  * existing transactions. We must not complete the detach action until
19419  * all such queries are complete (otherwise we would present them with an
19420  * inconsistent view of catalogs).
19421  */
19422  WaitForOlderSnapshots(snap->xmin, false);
19423 
19424  DetachPartitionFinalize(rel, partRel, true, InvalidOid);
19425 
19426  ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
19427 
19428  table_close(partRel, NoLock);
19429 
19430  return address;
19431 }
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition: indexcmds.c:424
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 14721 of file tablecmds.c.

14722 {
14723  mark_index_clustered(rel, InvalidOid, false);
14724 }

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

8958 {
8959  HeapTuple tuple;
8960  Form_pg_attribute targetatt;
8962  List *children;
8963  ObjectAddress object;
8964  bool is_expr;
8965 
8966  /* At top level, permission check was done in ATPrepCmd, else do it */
8967  if (recursing)
8969 
8970  /* Initialize addrs on the first invocation */
8971  Assert(!recursing || addrs != NULL);
8972 
8973  /* since this function recurses, it could be driven to stack overflow */
8975 
8976  if (!recursing)
8977  addrs = new_object_addresses();
8978 
8979  /*
8980  * get the number of the attribute
8981  */
8982  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8983  if (!HeapTupleIsValid(tuple))
8984  {
8985  if (!missing_ok)
8986  {
8987  ereport(ERROR,
8988  (errcode(ERRCODE_UNDEFINED_COLUMN),
8989  errmsg("column \"%s\" of relation \"%s\" does not exist",
8990  colName, RelationGetRelationName(rel))));
8991  }
8992  else
8993  {
8994  ereport(NOTICE,
8995  (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
8996  colName, RelationGetRelationName(rel))));
8997  return InvalidObjectAddress;
8998  }
8999  }
9000  targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
9001 
9002  attnum = targetatt->attnum;
9003 
9004  /* Can't drop a system attribute */
9005  if (attnum <= 0)
9006  ereport(ERROR,
9007  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
9008  errmsg("cannot drop system column \"%s\"",
9009  colName)));
9010 
9011  /*
9012  * Don't drop inherited columns, unless recursing (presumably from a drop
9013  * of the parent column)
9014  */
9015  if (targetatt->attinhcount > 0 && !recursing)
9016  ereport(ERROR,
9017  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9018  errmsg("cannot drop inherited column \"%s\"",
9019  colName)));
9020 
9021  /*
9022  * Don't drop columns used in the partition key, either. (If we let this
9023  * go through, the key column's dependencies would cause a cascaded drop
9024  * of the whole table, which is surely not what the user expected.)
9025  */
9026  if (has_partition_attrs(rel,
9028  &is_expr))
9029  ereport(ERROR,
9030  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9031  errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9032  colName, RelationGetRelationName(rel))));
9033 
9034  ReleaseSysCache(tuple);
9035 
9036  /*
9037  * Propagate to children as appropriate. Unlike most other ALTER
9038  * routines, we have to do this one level of recursion at a time; we can't
9039  * use find_all_inheritors to do it in one pass.
9040  */
9041  children =
9043 
9044  if (children)
9045  {
9046  Relation attr_rel;
9047  ListCell *child;
9048 
9049  /*
9050  * In case of a partitioned table, the column must be dropped from the
9051  * partitions as well.
9052  */
9053  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9054  ereport(ERROR,
9055  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
9056  errmsg("cannot drop column from only the partitioned table when partitions exist"),
9057  errhint("Do not specify the ONLY keyword.")));
9058 
9059  attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
9060  foreach(child, children)
9061  {
9062  Oid childrelid = lfirst_oid(child);
9063  Relation childrel;
9064  Form_pg_attribute childatt;
9065 
9066  /* find_inheritance_children already got lock */
9067  childrel = table_open(childrelid, NoLock);
9068  CheckAlterTableIsSafe(childrel);
9069 
9070  tuple = SearchSysCacheCopyAttName(childrelid, colName);
9071  if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9072  elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9073  colName, childrelid);
9074  childatt = (Form_pg_attribute) GETSTRUCT(tuple);
9075 
9076  if (childatt->attinhcount <= 0) /* shouldn't happen */
9077  elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9078  childrelid, colName);
9079 
9080  if (recurse)
9081  {
9082  /*
9083  * If the child column has other definition sources, just
9084  * decrement its inheritance count; if not, recurse to delete
9085  * it.
9086  */
9087  if (childatt->attinhcount == 1 && !childatt->attislocal)
9088  {
9089  /* Time to delete this child column, too */
9090  ATExecDropColumn(wqueue, childrel, colName,
9091  behavior, true, true,
9092  false, lockmode, addrs);
9093  }
9094  else
9095  {
9096  /* Child column must survive my deletion */
9097  childatt->attinhcount--;
9098 
9099  CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9100 
9101  /* Make update visible */
9103  }
9104  }
9105  else
9106  {
9107  /*
9108  * If we were told to drop ONLY in this table (no recursion),
9109  * we need to mark the inheritors' attributes as locally
9110  * defined rather than inherited.
9111  */
9112  childatt->attinhcount--;
9113  childatt->attislocal = true;
9114 
9115  CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9116 
9117  /* Make update visible */
9119  }
9120 
9121  heap_freetuple(tuple);
9122 
9123  table_close(childrel, NoLock);
9124  }
9125  table_close(attr_rel, RowExclusiveLock);
9126  }
9127 
9128  /* Add object to delete */
9129  object.classId = RelationRelationId;
9130  object.objectId = RelationGetRelid(rel);
9131  object.objectSubId = attnum;
9132  add_exact_object_address(&object, addrs);
9133 
9134  if (!recursing)
9135  {
9136  /* Recursion has ended, drop everything that was collected */
9137  performMultipleDeletions(addrs, behavior, 0);
9138  free_object_addresses(addrs);
9139  }
9140 
9141  return object;
9142 }
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(), CheckAlterTableIsSafe(), CommandCounterIncrement(), elog, ereport, errcode(), errhint(), errmsg(), ERROR, find_inheritance_children(), FirstLowInvalidHeapAttributeNumber, free_object_addresses(), GETSTRUCT, has_partition_attrs(), heap_freetuple(), HeapTupleIsValid, InvalidObjectAddress, lfirst_oid, new_object_addresses(), NoLock, NOTICE, performMultipleDeletions(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCacheAttName(), SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecDropConstraint()

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

Definition at line 12428 of file tablecmds.c.

12432 {
12433  List *children;
12434  Relation conrel;
12435  Form_pg_constraint con;
12436  SysScanDesc scan;
12437  ScanKeyData skey[3];
12438  HeapTuple tuple;
12439  bool found = false;
12440  bool is_no_inherit_constraint = false;
12441  char contype;
12442 
12443  /* At top level, permission check was done in ATPrepCmd, else do it */
12444  if (recursing)
12446 
12447  conrel = table_open(ConstraintRelationId, RowExclusiveLock);
12448 
12449  /*
12450  * Find and drop the target constraint
12451  */
12452  ScanKeyInit(&skey[0],
12453  Anum_pg_constraint_conrelid,
12454  BTEqualStrategyNumber, F_OIDEQ,
12456  ScanKeyInit(&skey[1],
12457  Anum_pg_constraint_contypid,
12458  BTEqualStrategyNumber, F_OIDEQ,
12460  ScanKeyInit(&skey[2],
12461  Anum_pg_constraint_conname,
12462  BTEqualStrategyNumber, F_NAMEEQ,
12463  CStringGetDatum(constrName));
12464  scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12465  true, NULL, 3, skey);
12466 
12467  /* There can be at most one matching row */
12468  if (HeapTupleIsValid(tuple = systable_getnext(scan)))
12469  {
12470  ObjectAddress conobj;
12471 
12472  con = (Form_pg_constraint) GETSTRUCT(tuple);
12473 
12474  /* Don't drop inherited constraints */
12475  if (con->coninhcount > 0 && !recursing)
12476  ereport(ERROR,
12477  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12478  errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
12479  constrName, RelationGetRelationName(rel))));
12480 
12481  is_no_inherit_constraint = con->connoinherit;
12482  contype = con->contype;
12483 
12484  /*
12485  * If it's a foreign-key constraint, we'd better lock the referenced
12486  * table and check that that's not in use, just as we've already done
12487  * for the constrained table (else we might, eg, be dropping a trigger
12488  * that has unfired events). But we can/must skip that in the
12489  * self-referential case.
12490  */
12491  if (contype == CONSTRAINT_FOREIGN &&
12492  con->confrelid != RelationGetRelid(rel))
12493  {
12494  Relation frel;
12495 
12496  /* Must match lock taken by RemoveTriggerById: */
12497  frel = table_open(con->confrelid, AccessExclusiveLock);
12498  CheckAlterTableIsSafe(frel);
12499  table_close(frel, NoLock);
12500  }
12501 
12502  /*
12503  * Perform the actual constraint deletion
12504  */
12505  conobj.classId = ConstraintRelationId;
12506  conobj.objectId = con->oid;
12507  conobj.objectSubId = 0;
12508 
12509  performDeletion(&conobj, behavior, 0);
12510 
12511  found = true;
12512  }
12513 
12514  systable_endscan(scan);
12515 
12516  if (!found)
12517  {
12518  if (!missing_ok)
12519  {
12520  ereport(ERROR,
12521  (errcode(ERRCODE_UNDEFINED_OBJECT),
12522  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12523  constrName, RelationGetRelationName(rel))));
12524  }
12525  else
12526  {
12527  ereport(NOTICE,
12528  (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
12529  constrName, RelationGetRelationName(rel))));
12530  table_close(conrel, RowExclusiveLock);
12531  return;
12532  }
12533  }
12534 
12535  /*
12536  * For partitioned tables, non-CHECK inherited constraints are dropped via
12537  * the dependency mechanism, so we're done here.
12538  */
12539  if (contype != CONSTRAINT_CHECK &&
12540  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12541  {
12542  table_close(conrel, RowExclusiveLock);
12543  return;
12544  }
12545 
12546  /*
12547  * Propagate to children as appropriate. Unlike most other ALTER
12548  * routines, we have to do this one level of recursion at a time; we can't
12549  * use find_all_inheritors to do it in one pass.
12550  */
12551  if (!is_no_inherit_constraint)
12552  children = find_inheritance_children(RelationGetRelid(rel), lockmode);
12553  else
12554  children = NIL;
12555 
12556  /*
12557  * For a partitioned table, if partitions exist and we are told not to
12558  * recurse, it's a user error. It doesn't make sense to have a constraint
12559  * be defined only on the parent, especially if it's a partitioned table.
12560  */
12561  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
12562  children != NIL && !recurse)
12563  ereport(ERROR,
12564  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12565  errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
12566  errhint("Do not specify the ONLY keyword.")));
12567 
12568  foreach_oid(childrelid, children)
12569  {
12570  Relation childrel;
12571  HeapTuple copy_tuple;
12572 
12573  /* find_inheritance_children already got lock */
12574  childrel = table_open(childrelid, NoLock);
12575  CheckAlterTableIsSafe(childrel);
12576 
12577  ScanKeyInit(&skey[0],
12578  Anum_pg_constraint_conrelid,
12579  BTEqualStrategyNumber, F_OIDEQ,
12580  ObjectIdGetDatum(childrelid));
12581  ScanKeyInit(&skey[1],
12582  Anum_pg_constraint_contypid,
12583  BTEqualStrategyNumber, F_OIDEQ,
12585  ScanKeyInit(&skey[2],
12586  Anum_pg_constraint_conname,
12587  BTEqualStrategyNumber, F_NAMEEQ,
12588  CStringGetDatum(constrName));
12589  scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
12590  true, NULL, 3, skey);
12591 
12592  /* There can be at most one matching row */
12593  if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12594  ereport(ERROR,
12595  (errcode(ERRCODE_UNDEFINED_OBJECT),
12596  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12597  constrName,
12598  RelationGetRelationName(childrel))));
12599 
12600  copy_tuple = heap_copytuple(tuple);
12601 
12602  systable_endscan(scan);
12603 
12604  con = (Form_pg_constraint) GETSTRUCT(copy_tuple);
12605 
12606  /* Right now only CHECK constraints can be inherited */
12607  if (con->contype != CONSTRAINT_CHECK)
12608  elog(ERROR, "inherited constraint is not a CHECK constraint");
12609 
12610  if (con->coninhcount <= 0) /* shouldn't happen */
12611  elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
12612  childrelid, constrName);
12613 
12614  if (recurse)
12615  {
12616  /*
12617  * If the child constraint has other definition sources, just
12618  * decrement its inheritance count; if not, recurse to delete it.
12619  */
12620  if (con->coninhcount == 1 && !con->conislocal)
12621  {
12622  /* Time to delete this child constraint, too */
12623  ATExecDropConstraint(childrel, constrName, behavior,
12624  true, true,
12625  false, lockmode);
12626  }
12627  else
12628  {
12629  /* Child constraint must survive my deletion */
12630  con->coninhcount--;
12631  CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
12632 
12633  /* Make update visible */
12635  }
12636  }
12637  else
12638  {
12639  /*
12640  * If we were told to drop ONLY in this table (no recursion), we
12641  * need to mark the inheritors' constraints as locally defined
12642  * rather than inherited.
12643  */
12644  con->coninhcount--;
12645  con->conislocal = true;
12646 
12647  CatalogTupleUpdate(conrel, &copy_tuple->t_self, copy_tuple);
12648 
12649  /* Make update visible */
12651  }
12652 
12653  heap_freetuple(copy_tuple);
12654 
12655  table_close(childrel, NoLock);
12656  }
12657 
12658  table_close(conrel, RowExclusiveLock);
12659 }
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition: dependency.c:273
#define foreach_oid(var, lst)
Definition: pg_list.h:471

References AccessExclusiveLock, AT_DropConstraint, ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_TABLE, BTEqualStrategyNumber, CatalogTupleUpdate(), CheckAlterTableIsSafe(), ObjectAddress::classId, CommandCounterIncrement(), CStringGetDatum(), elog, ereport, errcode(), errhint(), errmsg(), ERROR, find_inheritance_children(), foreach_oid, GETSTRUCT, heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvalidOid, NIL, NoLock, NOTICE, ObjectAddress::objectId, ObjectIdGetDatum(), ObjectAddress::objectSubId, performDeletion(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, 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 8494 of file tablecmds.c.

8495 {
8496  HeapTuple tuple;
8497  Form_pg_attribute attTup;
8499  Relation attrelation;
8500  Oid attrdefoid;
8501  ObjectAddress address;
8502 
8503  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8504  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8505  if (!HeapTupleIsValid(tuple))
8506  ereport(ERROR,
8507  (errcode(ERRCODE_UNDEFINED_COLUMN),
8508  errmsg("column \"%s\" of relation \"%s\" does not exist",
8509  colName, RelationGetRelationName(rel))));
8510 
8511  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8512  attnum = attTup->attnum;
8513 
8514  if (attnum <= 0)
8515  ereport(ERROR,
8516  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8517  errmsg("cannot alter system column \"%s\"",
8518  colName)));
8519 
8520  if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8521  {
8522  if (!missing_ok)
8523  ereport(ERROR,
8524  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8525  errmsg("column \"%s\" of relation \"%s\" is not a stored generated column",
8526  colName, RelationGetRelationName(rel))));
8527  else
8528  {
8529  ereport(NOTICE,
8530  (errmsg("column \"%s\" of relation \"%s\" is not a stored generated column, skipping",
8531  colName, RelationGetRelationName(rel))));
8532  heap_freetuple(tuple);
8533  table_close(attrelation, RowExclusiveLock);
8534  return InvalidObjectAddress;
8535  }
8536  }
8537 
8538  /*
8539  * Mark the column as no longer generated. (The atthasdef flag needs to
8540  * get cleared too, but RemoveAttrDefault will handle that.)
8541  */
8542  attTup->attgenerated = '\0';
8543  CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8544 
8545  InvokeObjectPostAlterHook(RelationRelationId,
8546  RelationGetRelid(rel),
8547  attnum);
8548  heap_freetuple(tuple);
8549 
8550  table_close(attrelation, RowExclusiveLock);
8551 
8552  /*
8553  * Drop the dependency records of the GENERATED expression, in particular
8554  * its INTERNAL dependency on the column, which would otherwise cause
8555  * dependency.c to refuse to perform the deletion.
8556  */
8557  attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8558  if (!OidIsValid(attrdefoid))
8559  elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8560  RelationGetRelid(rel), attnum);
8561  (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8562 
8563  /* Make above changes visible */
8565 
8566  /*
8567  * Get rid of the GENERATED expression itself. We use RESTRICT here for
8568  * safety, but at present we do not expect anything to depend on the
8569  * default.
8570  */
8572  false, false);
8573 
8574  ObjectAddressSubSet(address, RelationRelationId,
8575  RelationGetRelid(rel), attnum);
8576  return address;
8577 }

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

8223 {
8224  HeapTuple tuple;
8225  Form_pg_attribute attTup;
8227  Relation attrelation;
8228  ObjectAddress address;
8229  Oid seqid;
8230  ObjectAddress seqaddress;
8231  bool ispartitioned;
8232 
8233  ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8234  if (ispartitioned && !recurse)
8235  ereport(ERROR,
8236  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8237  errmsg("cannot drop identity from a column of only the partitioned table"),
8238  errhint("Do not specify the ONLY keyword.")));
8239 
8240  if (rel->rd_rel->relispartition && !recursing)
8241  ereport(ERROR,
8242  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8243  errmsg("cannot drop identity from a column of a partition"));
8244 
8245  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8246  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8247  if (!HeapTupleIsValid(tuple))
8248  ereport(ERROR,
8249  (errcode(ERRCODE_UNDEFINED_COLUMN),
8250  errmsg("column \"%s\" of relation \"%s\" does not exist",
8251  colName, RelationGetRelationName(rel))));
8252 
8253  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8254  attnum = attTup->attnum;
8255 
8256  if (attnum <= 0)
8257  ereport(ERROR,
8258  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8259  errmsg("cannot alter system column \"%s\"",
8260  colName)));
8261 
8262  if (!attTup->attidentity)
8263  {
8264  if (!missing_ok)
8265  ereport(ERROR,
8266  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8267  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8268  colName, RelationGetRelationName(rel))));
8269  else
8270  {
8271  ereport(NOTICE,
8272  (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8273  colName, RelationGetRelationName(rel))));
8274  heap_freetuple(tuple);
8275  table_close(attrelation, RowExclusiveLock);
8276  return InvalidObjectAddress;
8277  }
8278  }
8279 
8280  attTup->attidentity = '\0';
8281  CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8282 
8283  InvokeObjectPostAlterHook(RelationRelationId,
8284  RelationGetRelid(rel),
8285  attTup->attnum);
8286  ObjectAddressSubSet(address, RelationRelationId,
8287  RelationGetRelid(rel), attnum);
8288  heap_freetuple(tuple);
8289 
8290  table_close(attrelation, RowExclusiveLock);
8291 
8292  /*
8293  * Recurse to drop the identity from column in partitions. Identity is
8294  * not inherited in regular inheritance children so ignore them.
8295  */
8296  if (recurse && ispartitioned)
8297  {
8298  List *children;
8299  ListCell *lc;
8300 
8301  children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8302 
8303  foreach(lc, children)
8304  {
8305  Relation childrel;
8306 
8307  childrel = table_open(lfirst_oid(lc), NoLock);
8308  ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8309  table_close(childrel, NoLock);
8310  }
8311  }
8312 
8313  if (!recursing)
8314  {
8315  /* drop the internal sequence */
8316  seqid = getIdentitySequence(rel, attnum, false);
8317  deleteDependencyRecordsForClass(RelationRelationId, seqid,
8318  RelationRelationId, DEPENDENCY_INTERNAL);
8320  seqaddress.classId = RelationRelationId;
8321  seqaddress.objectId = seqid;
8322  seqaddress.objectSubId = 0;
8324  }
8325 
8326  return address;
8327 }
#define PERFORM_DELETION_INTERNAL
Definition: dependency.h:92
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition: pg_depend.c:352
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition: pg_depend.c:946

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

15979 {
15980  ObjectAddress address;
15981  Relation parent_rel;
15982 
15983  if (rel->rd_rel->relispartition)
15984  ereport(ERROR,
15985  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15986  errmsg("cannot change inheritance of a partition")));
15987 
15988  /*
15989  * AccessShareLock on the parent is probably enough, seeing that DROP
15990  * TABLE doesn't lock parent tables at all. We need some lock since we'll
15991  * be inspecting the parent's schema.
15992  */
15993  parent_rel = table_openrv(parent, AccessShareLock);
15994 
15995  /*
15996  * We don't bother to check ownership of the parent table --- ownership of
15997  * the child is presumed enough rights.
15998  */
15999 
16000  /* Off to RemoveInheritance() where most of the work happens */
16001  RemoveInheritance(rel, parent_rel, false);
16002 
16003  ObjectAddressSet(address, RelationRelationId,
16004  RelationGetRelid(parent_rel));
16005 
16006  /* keep our lock on the parent relation until commit */
16007  table_close(parent_rel, NoLock);
16008 
16009  return address;
16010 }

References AccessShareLock, ereport, errcode(), errmsg(), ERROR, NoLock, ObjectAddressSet, RelationData::rd_rel, RelationGetRelid, RemoveInheritance(), table_close(), and table_openrv().

Referenced by ATExecCmd().

◆ ATExecDropNotNull()

static ObjectAddress ATExecDropNotNull ( Relation  rel,
const char *  colName,
LOCKMODE  lockmode 
)
static

Definition at line 7531 of file tablecmds.c.

7532 {
7533  HeapTuple tuple;
7534  Form_pg_attribute attTup;
7536  Relation attr_rel;
7537  List *indexoidlist;
7538  ObjectAddress address;
7539 
7540  /*
7541  * lookup the attribute
7542  */
7543  attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7544 
7545  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7546  if (!HeapTupleIsValid(tuple))
7547  ereport(ERROR,
7548  (errcode(ERRCODE_UNDEFINED_COLUMN),
7549  errmsg("column \"%s\" of relation \"%s\" does not exist",
7550  colName, RelationGetRelationName(rel))));
7551  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
7552  attnum = attTup->attnum;
7553 
7554  /* Prevent them from altering a system attribute */
7555  if (attnum <= 0)
7556  ereport(ERROR,
7557  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7558  errmsg("cannot alter system column \"%s\"",
7559  colName)));
7560 
7561  if (attTup->attidentity)
7562  ereport(ERROR,
7563  (errcode(ERRCODE_SYNTAX_ERROR),
7564  errmsg("column \"%s\" of relation \"%s\" is an identity column",
7565  colName, RelationGetRelationName(rel))));
7566 
7567  /*
7568  * Check that the attribute is not in a primary key or in an index used as
7569  * a replica identity.
7570  *
7571  * Note: we'll throw error even if the pkey index is not valid.
7572  */
7573 
7574  /* Loop over all indexes on the relation */
7575  indexoidlist = RelationGetIndexList(rel);
7576 
7577  foreach_oid(indexoid, indexoidlist)
7578  {
7579  HeapTuple indexTuple;
7580  Form_pg_index indexStruct;
7581 
7582  indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
7583  if (!HeapTupleIsValid(indexTuple))
7584  elog(ERROR, "cache lookup failed for index %u", indexoid);
7585  indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
7586 
7587  /*
7588  * If the index is not a primary key or an index used as replica
7589  * identity, skip the check.
7590  */
7591  if (indexStruct->indisprimary || indexStruct->indisreplident)
7592  {
7593  /*
7594  * Loop over each attribute in the primary key or the index used
7595  * as replica identity and see if it matches the to-be-altered
7596  * attribute.
7597  */
7598  for (int i = 0; i < indexStruct->indnkeyatts; i++)
7599  {
7600  if (indexStruct->indkey.values[i] == attnum)
7601  {
7602  if (indexStruct->indisprimary)
7603  ereport(ERROR,
7604  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7605  errmsg("column \"%s\" is in a primary key",
7606  colName)));
7607  else
7608  ereport(ERROR,
7609  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7610  errmsg("column \"%s\" is in index used as replica identity",
7611  colName)));
7612  }
7613  }
7614  }
7615 
7616  ReleaseSysCache(indexTuple);
7617  }
7618 
7619  list_free(indexoidlist);
7620 
7621  /* If rel is partition, shouldn't drop NOT NULL if parent has the same */
7622  if (rel->rd_rel->relispartition)
7623  {
7624  Oid parentId = get_partition_parent(RelationGetRelid(rel), false);
7625  Relation parent = table_open(parentId, AccessShareLock);
7626  TupleDesc tupDesc = RelationGetDescr(parent);
7627  AttrNumber parent_attnum;
7628 
7629  parent_attnum = get_attnum(parentId, colName);
7630  if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7631  ereport(ERROR,
7632  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7633  errmsg("column \"%s\" is marked NOT NULL in parent table",
7634  colName)));
7635  table_close(parent, AccessShareLock);
7636  }
7637 
7638  /*
7639  * Okay, actually perform the catalog change ... if needed
7640  */
7641  if (attTup->attnotnull)
7642  {
7643  attTup->attnotnull = false;
7644 
7645  CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7646 
7647  ObjectAddressSubSet(address, RelationRelationId,
7648  RelationGetRelid(rel), attnum);
7649  }
7650  else
7651  address = InvalidObjectAddress;
7652 
7653  InvokeObjectPostAlterHook(RelationRelationId,
7654  RelationGetRelid(rel), attnum);
7655 
7656  table_close(attr_rel, RowExclusiveLock);
7657 
7658  return address;
7659 }
FormData_pg_index * Form_pg_index
Definition: pg_index.h:70

References AccessShareLock, attnotnull, attnum, CatalogTupleUpdate(), elog, ereport, errcode(), errmsg(), ERROR, foreach_oid, get_attnum(), get_partition_parent(), GETSTRUCT, HeapTupleIsValid, i, InvalidObjectAddress, InvokeObjectPostAlterHook, list_free(), ObjectAddressSubSet, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetDescr, RelationGetIndexList(), RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), 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 16465 of file tablecmds.c.

16466 {
16467  Oid relid = RelationGetRelid(rel);
16468  Relation relationRelation;
16469  HeapTuple tuple;
16470 
16471  if (!OidIsValid(rel->rd_rel->reloftype))
16472  ereport(ERROR,
16473  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16474  errmsg("\"%s\" is not a typed table",
16475  RelationGetRelationName(rel))));
16476 
16477  /*
16478  * We don't bother to check ownership of the type --- ownership of the
16479  * table is presumed enough rights. No lock required on the type, either.
16480  */
16481 
16482  drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
16484 
16485  /* Clear pg_class.reloftype */
16486  relationRelation = table_open(RelationRelationId, RowExclusiveLock);
16487  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16488  if (!HeapTupleIsValid(tuple))
16489  elog(ERROR, "cache lookup failed for relation %u", relid);
16490  ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
16491  CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
16492 
16493  InvokeObjectPostAlterHook(RelationRelationId, relid, 0);
16494 
16495  heap_freetuple(tuple);
16496  table_close(relationRelation, RowExclusiveLock);
16497 }

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

15461 {
15462  EnableDisableRule(rel, rulename, fires_when);
15463 
15464  InvokeObjectPostAlterHook(RelationRelationId,
15465  RelationGetRelid(rel), 0);
15466 }
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 15441 of file tablecmds.c.

15444 {
15445  EnableDisableTrigger(rel, trigname, InvalidOid,
15446  fires_when, skip_system, recurse,
15447  lockmode);
15448 
15449  InvokeObjectPostAlterHook(RelationRelationId,
15450  RelationGetRelid(rel), 0);
15451 }
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 16735 of file tablecmds.c.

16736 {
16737  Relation pg_class;
16738  Oid relid;
16739  HeapTuple tuple;
16740 
16741  relid = RelationGetRelid(rel);
16742 
16743  pg_class = table_open(RelationRelationId, RowExclusiveLock);
16744 
16745  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16746 
16747  if (!HeapTupleIsValid(tuple))
16748  elog(ERROR, "cache lookup failed for relation %u", relid);
16749 
16750  ((Form_pg_class) GETSTRUCT(tuple))->relforcerowsecurity = force_rls;
16751  CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16752 
16753  InvokeObjectPostAlterHook(RelationRelationId,
16754  RelationGetRelid(rel), 0);
16755 
16756  table_close(pg_class, RowExclusiveLock);
16757  heap_freetuple(tuple);
16758 }

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

16765 {
16766  Relation ftrel;
16767  ForeignServer *server;
16768  ForeignDataWrapper *fdw;
16769  HeapTuple tuple;
16770  bool isnull;
16771  Datum repl_val[Natts_pg_foreign_table];
16772  bool repl_null[Natts_pg_foreign_table];
16773  bool repl_repl[Natts_pg_foreign_table];
16774  Datum datum;
16775  Form_pg_foreign_table tableform;
16776 
16777  if (options == NIL)
16778  return;
16779 
16780  ftrel = table_open(ForeignTableRelationId, RowExclusiveLock);
16781 
16782  tuple = SearchSysCacheCopy1(FOREIGNTABLEREL,
16783  ObjectIdGetDatum(rel->rd_id));
16784  if (!HeapTupleIsValid(tuple))
16785  ereport(ERROR,
16786  (errcode(ERRCODE_UNDEFINED_OBJECT),
16787  errmsg("foreign table \"%s\" does not exist",
16788  RelationGetRelationName(rel))));
16789  tableform = (Form_pg_foreign_table) GETSTRUCT(tuple);
16790  server = GetForeignServer(tableform->ftserver);
16791  fdw = GetForeignDataWrapper(server->fdwid);
16792 
16793  memset(repl_val, 0, sizeof(repl_val));
16794  memset(repl_null, false, sizeof(repl_null));
16795  memset(repl_repl, false, sizeof(repl_repl));
16796 
16797  /* Extract the current options */
16798  datum = SysCacheGetAttr(FOREIGNTABLEREL,
16799  tuple,
16800  Anum_pg_foreign_table_ftoptions,
16801  &isnull);
16802  if (isnull)
16803  datum = PointerGetDatum(NULL);
16804 
16805  /* Transform the options */
16806  datum = transformGenericOptions(ForeignTableRelationId,
16807  datum,
16808  options,
16809  fdw->fdwvalidator);
16810 
16811  if (PointerIsValid(DatumGetPointer(datum)))
16812  repl_val[Anum_pg_foreign_table_ftoptions - 1] = datum;
16813  else
16814  repl_null[Anum_pg_foreign_table_ftoptions - 1] = true;
16815 
16816  repl_repl[Anum_pg_foreign_table_ftoptions - 1] = true;
16817 
16818  /* Everything looks good - update the tuple */
16819 
16820  tuple = heap_modify_tuple(tuple, RelationGetDescr(ftrel),
16821  repl_val, repl_null, repl_repl);
16822 
16823  CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
16824 
16825  /*
16826  * Invalidate relcache so that all sessions will refresh any cached plans
16827  * that might depend on the old options.
16828  */
16830 
16831  InvokeObjectPostAlterHook(ForeignTableRelationId,
16832  RelationGetRelid(rel), 0);
16833 
16834  table_close(ftrel, RowExclusiveLock);
16835 
16836  heap_freetuple(tuple);
16837 }

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

20646 {
20647  Relation newPartRel;
20648  ListCell *listptr;
20649  List *mergingPartitionsList = NIL;
20650  Oid defaultPartOid;
20651  Oid namespaceId;
20652  Oid existingRelid;
20653 
20654  /*
20655  * Lock all merged partitions, check them and create list with partitions
20656  * contexts.
20657  */
20658  foreach(listptr, cmd->partlist)
20659  {
20660  RangeVar *name = (RangeVar *) lfirst(listptr);
20661  Relation mergingPartition;
20662 
20663  /*
20664  * We are going to detach and remove this partition: need to use
20665  * exclusive lock for preventing DML-queries to the partition.
20666  */
20667  mergingPartition = table_openrv(name, AccessExclusiveLock);
20668 
20669  /* Store a next merging partition into the list. */
20670  mergingPartitionsList = lappend(mergingPartitionsList,
20671  mergingPartition);
20672  }
20673 
20674  /*
20675  * Look up the namespace in which we are supposed to create the partition,
20676  * check we have permission to create there, lock it against concurrent
20677  * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
20678  * namespace is selected.
20679  */
20680  cmd->name->relpersistence = rel->rd_rel->relpersistence;
20681  namespaceId =
20683 
20684  /*
20685  * Check if this name is already taken. This helps us to detect the
20686  * situation when one of the merging partitions has the same name as the
20687  * new partition. Otherwise, this would fail later on anyway but catching
20688  * this here allows us to emit a nicer error message.
20689  */
20690  existingRelid = get_relname_relid(cmd->name->relname, namespaceId);
20691 
20692  if (OidIsValid(existingRelid))
20693  {
20694  Relation sameNamePartition = NULL;
20695 
20696  foreach_ptr(RelationData, mergingPartition, mergingPartitionsList)
20697  {
20698  if (RelationGetRelid(mergingPartition) == existingRelid)
20699  {
20700  sameNamePartition = mergingPartition;
20701  break;
20702  }
20703  }
20704 
20705  if (sameNamePartition)
20706  {
20707  /*
20708  * The new partition has the same name as one of merging
20709  * partitions.
20710  */
20711  char tmpRelName[NAMEDATALEN];
20712 
20713  /* Generate temporary name. */
20714  sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
20715 
20716  /*
20717  * Rename the existing partition with a temporary name, leaving it
20718  * free for the new partition. We don't need to care about this
20719  * in the future because we're going to eventually drop the
20720  * existing partition anyway.
20721  */
20722  RenameRelationInternal(RelationGetRelid(sameNamePartition),
20723  tmpRelName, false, false);
20724 
20725  /*
20726  * We must bump the command counter to make the new partition
20727  * tuple visible for rename.
20728  */
20730  }
20731  else
20732  {
20733  ereport(ERROR,
20734  (errcode(ERRCODE_DUPLICATE_TABLE),
20735  errmsg("relation \"%s\" already exists", cmd->name->relname)));
20736  }
20737  }
20738 
20739  /* Detach all merged partitions. */
20740  defaultPartOid =
20742  foreach(listptr, mergingPartitionsList)
20743  {
20744  Relation mergingPartition = (Relation) lfirst(listptr);
20745 
20746  /* Remove the pg_inherits row first. */
20747  RemoveInheritance(mergingPartition, rel, false);
20748  /* Do the final part of detaching. */
20749  DetachPartitionFinalize(rel, mergingPartition, false, defaultPartOid);
20750  }
20751 
20752  /* Create table for new partition, use partitioned table as model. */
20753  newPartRel = createPartitionTable(cmd->name, rel, context);
20754 
20755  /* Copy data from merged partitions to new partition. */
20756  moveMergedTablesRows(rel, mergingPartitionsList, newPartRel);
20757 
20758  /* Drop the current partitions before attaching the new one. */
20759  foreach(listptr, mergingPartitionsList)
20760  {
20761  ObjectAddress object;
20762  Relation mergingPartition = (Relation) lfirst(listptr);
20763 
20764  /* Get relation id before table_close() call. */
20765  object.objectId = RelationGetRelid(mergingPartition);
20766  object.classId = RelationRelationId;
20767  object.objectSubId = 0;
20768 
20769  /* Keep the lock until commit. */
20770  table_close(mergingPartition, NoLock);
20771 
20772  performDeletion(&object, DROP_RESTRICT, 0);
20773  }
20774  list_free(mergingPartitionsList);
20775 
20776  /*
20777  * Attach a new partition to the partitioned table. wqueue = NULL:
20778  * verification for each cloned constraint is not needed.
20779  */
20780  attachPartitionTable(NULL, rel, newPartRel, cmd->bound);
20781 
20782  /* Keep the lock until commit. */
20783  table_close(newPartRel, NoLock);
20784 }
int MyProcPid
Definition: globals.c:46
#define foreach_ptr(type, var, lst)
Definition: pg_list.h:469
#define sprintf
Definition: port.h:240
struct RelationData * Relation
Definition: relcache.h:27
List * partlist
Definition: parsenodes.h:959
char relpersistence
Definition: primnodes.h:88
static void moveMergedTablesRows(Relation rel, List *mergingPartitionsList, Relation newPartRel)
Definition: tablecmds.c:20547
static Relation createPartitionTable(RangeVar *newPartName, Relation modelRel, AlterTableUtilityContext *context)
Definition: tablecmds.c:20325

References AccessExclusiveLock, attachPartitionTable(), PartitionCmd::bound, CommandCounterIncrement(), context, createPartitionTable(), DetachPartitionFinalize(), DROP_RESTRICT, ereport, errcode(), errmsg(), ERROR, foreach_ptr, get_default_oid_from_partdesc(), get_relname_relid(), lappend(), lfirst, list_free(), moveMergedTablesRows(), MyProcPid, name, PartitionCmd::name, NAMEDATALEN, NIL, NoLock, OidIsValid, PartitionCmd::partlist, performDeletion(), RangeVarGetAndCheckCreationNamespace(), RelationData::rd_rel, RelationGetPartitionDesc(), RelationGetRelid, RangeVar::relname, RangeVar::relpersistence, 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 16597 of file tablecmds.c.

16598 {
16599  Oid indexOid;
16600  Relation indexRel;
16601  int key;
16602 
16603  if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
16604  {
16605  relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16606  return;
16607  }
16608  else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
16609  {
16610  relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16611  return;
16612  }
16613  else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
16614  {
16615  relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
16616  return;
16617  }
16618  else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
16619  {
16620  /* fallthrough */ ;
16621  }
16622  else
16623  elog(ERROR, "unexpected identity type %u", stmt->identity_type);
16624 
16625  /* Check that the index exists */
16626  indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
16627  if (!OidIsValid(indexOid))
16628  ereport(ERROR,
16629  (errcode(ERRCODE_UNDEFINED_OBJECT),
16630  errmsg("index \"%s\" for table \"%s\" does not exist",
16631  stmt->name, RelationGetRelationName(rel))));
16632 
16633  indexRel = index_open(indexOid, ShareLock);
16634 
16635  /* Check that the index is on the relation we're altering. */
16636  if (indexRel->rd_index == NULL ||
16637  indexRel->rd_index->indrelid != RelationGetRelid(rel))
16638  ereport(ERROR,
16639  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16640  errmsg("\"%s\" is not an index for table \"%s\"",
16641  RelationGetRelationName(indexRel),
16642  RelationGetRelationName(rel))));
16643  /* The AM must support uniqueness, and the index must in fact be unique. */
16644  if (!indexRel->rd_indam->amcanunique ||
16645  !indexRel->rd_index->indisunique)
16646  ereport(ERROR,
16647  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16648  errmsg("cannot use non-unique index \"%s\" as replica identity",
16649  RelationGetRelationName(indexRel))));
16650  /* Deferred indexes are not guaranteed to be always unique. */
16651  if (!indexRel->rd_index->indimmediate)
16652  ereport(ERROR,
16653  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16654  errmsg("cannot use non-immediate index \"%s\" as replica identity",
16655  RelationGetRelationName(indexRel))));
16656  /* Expression indexes aren't supported. */
16657  if (RelationGetIndexExpressions(indexRel) != NIL)
16658  ereport(ERROR,
16659  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16660  errmsg("cannot use expression index \"%s\" as replica identity",
16661  RelationGetRelationName(indexRel))));
16662  /* Predicate indexes aren't supported. */
16663  if (RelationGetIndexPredicate(indexRel) != NIL)
16664  ereport(ERROR,
16665  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16666  errmsg("cannot use partial index \"%s\" as replica identity",
16667  RelationGetRelationName(indexRel))));
16668 
16669  /* Check index for nullable columns. */
16670  for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
16671  {
16672  int16 attno = indexRel->rd_index->indkey.values[key];
16673  Form_pg_attribute attr;
16674 
16675  /*
16676  * Reject any other system columns. (Going forward, we'll disallow
16677  * indexes containing such columns in the first place, but they might
16678  * exist in older branches.)
16679  */
16680  if (attno <= 0)
16681  ereport(ERROR,
16682  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
16683  errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
16684  RelationGetRelationName(indexRel), attno)));
16685 
16686  attr = TupleDescAttr(rel->rd_att, attno - 1);
16687  if (!attr->attnotnull)
16688  ereport(ERROR,
16689  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
16690  errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
16691  RelationGetRelationName(indexRel),
16692  NameStr(attr->attname))));
16693  }
16694 
16695  /* This index is suitable for use as a replica identity. Mark it. */
16696  relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
16697 
16698  index_close(indexRel, NoLock);
16699 }
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition: rel.h:524
List * RelationGetIndexPredicate(Relation relation)
Definition: relcache.c:5151
List * RelationGetIndexExpressions(Relation relation)
Definition: relcache.c:5038
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:16509

References IndexAmRoutine::amcanunique, elog, ereport, errcode(), errmsg(), ERROR, get_relname_relid(), index_close(), index_open(), IndexRelationGetNumberOfKeyAttributes, InvalidOid, sort-test::key, NameStr, NIL, NoLock, OidIsValid, RelationData::rd_att, RelationData::rd_indam, RelationData::rd_index, RelationData::rd_rel, relation_mark_replica_identity(), RelationGetIndexExpressions(), RelationGetIndexPredicate(), RelationGetRelationName, RelationGetRelid, ShareLock, stmt, and TupleDescAttr.

Referenced by ATExecCmd().

◆ ATExecSetAccessMethodNoStorage()

static void ATExecSetAccessMethodNoStorage ( Relation  rel,
Oid  newAccessMethodId 
)
static

Definition at line 14767 of file tablecmds.c.

14768 {
14769  Relation pg_class;
14770  Oid oldAccessMethodId;
14771  HeapTuple tuple;
14772  Form_pg_class rd_rel;
14773  Oid reloid = RelationGetRelid(rel);
14774 
14775  /*
14776  * Shouldn't be called on relations having storage; these are processed in
14777  * phase 3.
14778  */
14779  Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
14780 
14781  /* Get a modifiable copy of the relation's pg_class row. */
14782  pg_class = table_open(RelationRelationId, RowExclusiveLock);
14783 
14784  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
14785  if (!HeapTupleIsValid(tuple))
14786  elog(ERROR, "cache lookup failed for relation %u", reloid);
14787  rd_rel = (Form_pg_class) GETSTRUCT(tuple);
14788 
14789  /* Update the pg_class row. */
14790  oldAccessMethodId = rd_rel->relam;
14791  rd_rel->relam = newAccessMethodId;
14792 
14793  /* Leave if no update required */
14794  if (rd_rel->relam == oldAccessMethodId)
14795  {
14796  heap_freetuple(tuple);
14797  table_close(pg_class, RowExclusiveLock);
14798  return;
14799  }
14800 
14801  CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
14802 
14803  /*
14804  * Update the dependency on the new access method. No dependency is added
14805  * if the new access method is InvalidOid (default case). Be very careful
14806  * that this has to compare the previous value stored in pg_class with the
14807  * new one.
14808  */
14809  if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
14810  {
14811  ObjectAddress relobj,
14812  referenced;
14813 
14814  /*
14815  * New access method is defined and there was no dependency
14816  * previously, so record a new one.
14817  */
14818  ObjectAddressSet(relobj, RelationRelationId, reloid);
14819  ObjectAddressSet(referenced, AccessMethodRelationId, rd_rel->relam);
14820  recordDependencyOn(&relobj, &referenced, DEPENDENCY_NORMAL);
14821  }
14822  else if (OidIsValid(oldAccessMethodId) &&
14823  !OidIsValid(rd_rel->relam))
14824  {
14825  /*
14826  * There was an access method defined, and no new one, so just remove
14827  * the existing dependency.
14828  */
14829  deleteDependencyRecordsForClass(RelationRelationId, reloid,
14830  AccessMethodRelationId,
14832  }
14833  else
14834  {
14835  Assert(OidIsValid(oldAccessMethodId) &&
14836  OidIsValid(rd_rel->relam));
14837 
14838  /* Both are valid, so update the dependency */
14839  changeDependencyFor(RelationRelationId, reloid,
14840  AccessMethodRelationId,
14841  oldAccessMethodId, rd_rel->relam);
14842  }
14843 
14844  /* make the relam and dependency changes visible */
14846 
14847  InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
14848 
14849  heap_freetuple(tuple);
14850  table_close(pg_class, RowExclusiveLock);
14851 }

References Assert, CatalogTupleUpdate(), changeDependencyFor(), CommandCounterIncrement(), deleteDependencyRecordsForClass(), DEPENDENCY_NORMAL, elog, ERROR, GETSTRUCT, heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, RelationData::rd_rel, recordDependencyOn(), RelationGetRelid, RowExclusiveLock, SearchSysCacheCopy1, HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecSetCompression()

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

Definition at line 16845 of file tablecmds.c.

16849 {
16850  Relation attrel;
16851  HeapTuple tuple;
16852  Form_pg_attribute atttableform;
16854  char *compression;
16855  char cmethod;
16856  ObjectAddress address;
16857 
16858  compression = strVal(newValue);
16859 
16860  attrel = table_open(AttributeRelationId, RowExclusiveLock);
16861 
16862  /* copy the cache entry so we can scribble on it below */
16863  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), column);
16864  if (!HeapTupleIsValid(tuple))
16865  ereport(ERROR,
16866  (errcode(ERRCODE_UNDEFINED_COLUMN),
16867  errmsg("column \"%s\" of relation \"%s\" does not exist",
16868  column, RelationGetRelationName(rel))));
16869 
16870  /* prevent them from altering a system attribute */
16871  atttableform = (Form_pg_attribute) GETSTRUCT(tuple);
16872  attnum = atttableform->attnum;
16873  if (attnum <= 0)
16874  ereport(ERROR,
16875  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
16876  errmsg("cannot alter system column \"%s\"", column)));
16877 
16878  /*
16879  * Check that column type is compressible, then get the attribute
16880  * compression method code
16881  */
16882  cmethod = GetAttributeCompression(atttableform->atttypid, compression);
16883 
16884  /* update pg_attribute entry */
16885  atttableform->attcompression = cmethod;
16886  CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
16887 
16888  InvokeObjectPostAlterHook(RelationRelationId,
16889  RelationGetRelid(rel),
16890  attnum);
16891 
16892  /*
16893  * Apply the change to indexes as well (only for simple index columns,
16894  * matching behavior of index.c ConstructTupleDescriptor()).
16895  */
16896  SetIndexStorageProperties(rel, attrel, attnum,
16897  false, 0,
16898  true, cmethod,
16899  lockmode);
16900 
16901  heap_freetuple(tuple);
16902 
16903  table_close(attrel, RowExclusiveLock);
16904 
16905  /* make changes visible */
16907 
16908  ObjectAddressSubSet(address, RelationRelationId,
16909  RelationGetRelid(rel), attnum);
16910  return address;
16911 }
static char GetAttributeCompression(Oid atttypid, const char *compression)
Definition: tablecmds.c:19990
static void SetIndexStorageProperties(Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
Definition: tablecmds.c:8799

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

8337 {
8338  HeapTuple tuple;
8339  Form_pg_attribute attTup;
8341  Oid attrdefoid;
8342  ObjectAddress address;
8343  Expr *defval;
8345  RawColumnDefault *rawEnt;
8346 
8347  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8348  if (!HeapTupleIsValid(tuple))
8349  ereport(ERROR,
8350  (errcode(ERRCODE_UNDEFINED_COLUMN),
8351  errmsg("column \"%s\" of relation \"%s\" does not exist",
8352  colName, RelationGetRelationName(rel))));
8353 
8354  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8355  attnum = attTup->attnum;
8356 
8357  if (attnum <= 0)
8358  ereport(ERROR,
8359  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8360  errmsg("cannot alter system column \"%s\"",
8361  colName)));
8362 
8363  if (attTup->attgenerated != ATTRIBUTE_GENERATED_STORED)
8364  ereport(ERROR,
8365  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8366  errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8367  colName, RelationGetRelationName(rel))));
8368  ReleaseSysCache(tuple);
8369 
8370  /*
8371  * Clear all the missing values if we're rewriting the table, since this
8372  * renders them pointless.
8373  */
8374  RelationClearMissing(rel);
8375 
8376  /* make sure we don't conflict with later attribute modifications */
8378 
8379  /*
8380  * Find everything that depends on the column (constraints, indexes, etc),
8381  * and record enough information to let us recreate the objects after
8382  * rewrite.
8383  */
8385 
8386  /*
8387  * Drop the dependency records of the GENERATED expression, in particular
8388  * its INTERNAL dependency on the column, which would otherwise cause
8389  * dependency.c to refuse to perform the deletion.
8390  */
8391  attrdefoid = GetAttrDefaultOid(RelationGetRelid(rel), attnum);
8392  if (!OidIsValid(attrdefoid))
8393  elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8394  RelationGetRelid(rel), attnum);
8395  (void) deleteDependencyRecordsFor(AttrDefaultRelationId, attrdefoid, false);
8396 
8397  /* Make above changes visible */
8399 
8400  /*
8401  * Get rid of the GENERATED expression itself. We use RESTRICT here for
8402  * safety, but at present we do not expect anything to depend on the
8403  * expression.
8404  */
8406  false, false);
8407 
8408  /* Prepare to store the new expression, in the catalogs */
8409  rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault));
8410  rawEnt->attnum = attnum;
8411  rawEnt->raw_default = newExpr;
8412  rawEnt->missingMode = false;
8413  rawEnt->generated = ATTRIBUTE_GENERATED_STORED;
8414 
8415  /* Store the generated expression */
8417  false, true, false, NULL);
8418 
8419  /* Make above new expression visible */
8421 
8422  /* Prepare for table rewrite */
8423  defval = (Expr *) build_column_default(rel, attnum);
8424 
8426  newval->attnum = attnum;
8427  newval->expr = expression_planner(defval);
8428  newval->is_generated = true;
8429 
8430  tab->newvals = lappend(tab->newvals, newval);
8432 
8433  /* Drop any pg_statistic entry for the column */
8435 
8436  InvokeObjectPostAlterHook(RelationRelationId,
8437  RelationGetRelid(rel), attnum);
8438 
8439  ObjectAddressSubSet(address, RelationRelationId,
8440  RelationGetRelid(rel), attnum);
8441  return address;
8442 }

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

8106 {
8107  ListCell *option;
8108  DefElem *generatedEl = NULL;
8109  HeapTuple tuple;
8110  Form_pg_attribute attTup;
8112  Relation attrelation;
8113  ObjectAddress address;
8114  bool ispartitioned;
8115 
8116  ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8117  if (ispartitioned && !recurse)
8118  ereport(ERROR,
8119  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8120  errmsg("cannot change identity column of only the partitioned table"),
8121  errhint("Do not specify the ONLY keyword.")));
8122 
8123  if (rel->rd_rel->relispartition && !recursing)
8124  ereport(ERROR,
8125  errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8126  errmsg("cannot change identity column of a partition"));
8127 
8128  foreach(option, castNode(List, def))
8129  {
8130  DefElem *defel = lfirst_node(DefElem, option);
8131 
8132  if (strcmp(defel->defname, "generated") == 0)
8133  {
8134  if (generatedEl)
8135  ereport(ERROR,
8136  (errcode(ERRCODE_SYNTAX_ERROR),
8137  errmsg("conflicting or redundant options")));
8138  generatedEl = defel;
8139  }
8140  else
8141  elog(ERROR, "option \"%s\" not recognized",
8142  defel->defname);
8143  }
8144 
8145  /*
8146  * Even if there is nothing to change here, we run all the checks. There
8147  * will be a subsequent ALTER SEQUENCE that relies on everything being
8148  * there.
8149  */
8150 
8151  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8152  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8153  if (!HeapTupleIsValid(tuple))
8154  ereport(ERROR,
8155  (errcode(ERRCODE_UNDEFINED_COLUMN),
8156  errmsg("column \"%s\" of relation \"%s\" does not exist",
8157  colName, RelationGetRelationName(rel))));
8158 
8159  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8160  attnum = attTup->attnum;
8161 
8162  if (attnum <= 0)
8163  ereport(ERROR,
8164  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8165  errmsg("cannot alter system column \"%s\"",
8166  colName)));
8167 
8168  if (!attTup->attidentity)
8169  ereport(ERROR,
8170  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
8171  errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8172  colName, RelationGetRelationName(rel))));
8173 
8174  if (generatedEl)
8175  {
8176  attTup->attidentity = defGetInt32(generatedEl);
8177  CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8178 
8179  InvokeObjectPostAlterHook(RelationRelationId,
8180  RelationGetRelid(rel),
8181  attTup->attnum);
8182  ObjectAddressSubSet(address, RelationRelationId,
8183  RelationGetRelid(rel), attnum);
8184  }
8185  else
8186  address = InvalidObjectAddress;
8187 
8188  heap_freetuple(tuple);
8189  table_close(attrelation, RowExclusiveLock);
8190 
8191  /*
8192  * Recurse to propagate the identity change to partitions. Identity is not
8193  * inherited in regular inheritance children.
8194  */
8195  if (generatedEl && recurse && ispartitioned)
8196  {
8197  List *children;
8198  ListCell *lc;
8199 
8200  children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8201 
8202  foreach(lc, children)
8203  {
8204  Relation childrel;
8205 
8206  childrel = table_open(lfirst_oid(lc), NoLock);
8207  ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8208  table_close(childrel, NoLock);
8209  }
8210  }
8211 
8212  return address;
8213 }
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 ( AlteredTableInfo tab,
Relation  rel,
const char *  colName,
LOCKMODE  lockmode 
)
static

Definition at line 7735 of file tablecmds.c.

7737 {
7738  HeapTuple tuple;
7740  Relation attr_rel;
7741  ObjectAddress address;
7742 
7743  /*
7744  * lookup the attribute
7745  */
7746  attr_rel = table_open(AttributeRelationId, RowExclusiveLock);
7747 
7748  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
7749 
7750  if (!HeapTupleIsValid(tuple))
7751  ereport(ERROR,
7752  (errcode(ERRCODE_UNDEFINED_COLUMN),
7753  errmsg("column \"%s\" of relation \"%s\" does not exist",
7754  colName, RelationGetRelationName(rel))));
7755 
7756  attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum;
7757 
7758  /* Prevent them from altering a system attribute */
7759  if (attnum <= 0)
7760  ereport(ERROR,
7761  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
7762  errmsg("cannot alter system column \"%s\"",
7763  colName)));
7764 
7765  /*
7766  * Okay, actually perform the catalog change ... if needed
7767  */
7768  if (!((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull)
7769  {
7770  ((Form_pg_attribute) GETSTRUCT(tuple))->attnotnull = true;
7771 
7772  CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7773 
7774  /*
7775  * Ordinarily phase 3 must ensure that no NULLs exist in columns that
7776  * are set NOT NULL; however, if we can find a constraint which proves
7777  * this then we can skip that. We needn't bother looking if we've
7778  * already found that we must verify some other not-null constraint.
7779  */
7780  if (!tab->verify_new_notnull &&
7782  {
7783  /* Tell Phase 3 it needs to test the constraint */
7784  tab->verify_new_notnull = true;
7785  }
7786 
7787  ObjectAddressSubSet(address, RelationRelationId,
7788  RelationGetRelid(rel), attnum);
7789  }
7790  else
7791  address = InvalidObjectAddress;
7792 
7793  InvokeObjectPostAlterHook(RelationRelationId,
7794  RelationGetRelid(rel), attnum);
7795 
7796  table_close(attr_rel, RowExclusiveLock);
7797 
7798  return address;
7799 }
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition: tablecmds.c:7846

References attnotnull, attnum, CatalogTupleUpdate(), ereport, errcode(), errmsg(), ERROR, GETSTRUCT, HeapTupleIsValid, InvalidObjectAddress, InvokeObjectPostAlterHook, NotNullImpliedByRelConstraints(), ObjectAddressSubSet, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), table_open(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATExecCmd().

◆ ATExecSetOptions()

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

Definition at line 8720 of file tablecmds.c.

8722 {
8723  Relation attrelation;
8724  HeapTuple tuple,
8725  newtuple;
8726  Form_pg_attribute attrtuple;
8728  Datum datum,
8729  newOptions;
8730  bool isnull;
8731  ObjectAddress address;
8732  Datum repl_val[Natts_pg_attribute];
8733  bool repl_null[Natts_pg_attribute];
8734  bool repl_repl[Natts_pg_attribute];
8735 
8736  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8737 
8738  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8739 
8740  if (!HeapTupleIsValid(tuple))
8741  ereport(ERROR,
8742  (errcode(ERRCODE_UNDEFINED_COLUMN),
8743  errmsg("column \"%s\" of relation \"%s\" does not exist",
8744  colName, RelationGetRelationName(rel))));
8745  attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8746 
8747  attnum = attrtuple->attnum;
8748  if (attnum <= 0)
8749  ereport(ERROR,
8750  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8751  errmsg("cannot alter system column \"%s\"",
8752  colName)));
8753 
8754  /* Generate new proposed attoptions (text array) */
8755  datum = SysCacheGetAttr(ATTNAME, tuple, Anum_pg_attribute_attoptions,
8756  &isnull);
8757  newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
8758  castNode(List, options), NULL, NULL,
8759  false, isReset);
8760  /* Validate new options */
8761  (void) attribute_reloptions(newOptions, true);
8762 
8763  /* Build new tuple. */
8764  memset(repl_null, false, sizeof(repl_null));
8765  memset(repl_repl, false, sizeof(repl_repl));
8766  if (newOptions != (Datum) 0)
8767  repl_val[Anum_pg_attribute_attoptions - 1] = newOptions;
8768  else
8769  repl_null[Anum_pg_attribute_attoptions - 1] = true;
8770  repl_repl[Anum_pg_attribute_attoptions - 1] = true;
8771  newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8772  repl_val, repl_null, repl_repl);
8773 
8774  /* Update system catalog. */
8775  CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
8776 
8777  InvokeObjectPostAlterHook(RelationRelationId,
8778  RelationGetRelid(rel),
8779  attrtuple->attnum);
8780  ObjectAddressSubSet(address, RelationRelationId,
8781  RelationGetRelid(rel), attnum);
8782 
8783  heap_freetuple(newtuple);
8784 
8785  ReleaseSysCache(tuple);
8786 
8787  table_close(attrelation, RowExclusiveLock);
8788 
8789  return address;
8790 }
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 14887 of file tablecmds.c.

14889 {
14890  Oid relid;
14891  Relation pgclass;
14892  HeapTuple tuple;
14893  HeapTuple newtuple;
14894  Datum datum;
14895  bool isnull;
14896  Datum newOptions;
14897  Datum repl_val[Natts_pg_class];
14898  bool repl_null[Natts_pg_class];
14899  bool repl_repl[Natts_pg_class];
14900  static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
14901 
14902  if (defList == NIL && operation != AT_ReplaceRelOptions)
14903  return; /* nothing to do */
14904 
14905  pgclass = table_open(RelationRelationId, RowExclusiveLock);
14906 
14907  /* Fetch heap tuple */
14908  relid = RelationGetRelid(rel);
14909  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
14910  if (!HeapTupleIsValid(tuple))
14911  elog(ERROR, "cache lookup failed for relation %u", relid);
14912 
14913  if (operation == AT_ReplaceRelOptions)
14914  {
14915  /*
14916  * If we're supposed to replace the reloptions list, we just pretend
14917  * there were none before.
14918  */
14919  datum = (Datum) 0;
14920  isnull = true;
14921  }
14922  else
14923  {
14924  /* Get the old reloptions */
14925  datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
14926  &isnull);
14927  }
14928 
14929  /* Generate new proposed reloptions (text array) */
14930  newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
14931  defList, NULL, validnsps, false,
14932  operation == AT_ResetRelOptions);
14933 
14934  /* Validate */
14935  switch (rel->rd_rel->relkind)
14936  {
14937  case RELKIND_RELATION:
14938  case RELKIND_TOASTVALUE:
14939  case RELKIND_MATVIEW:
14940  (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
14941  break;
14942  case RELKIND_PARTITIONED_TABLE:
14943  (void) partitioned_table_reloptions(newOptions, true);
14944  break;
14945  case RELKIND_VIEW:
14946  (void) view_reloptions(newOptions, true);
14947  break;
14948  case RELKIND_INDEX:
14949  case RELKIND_PARTITIONED_INDEX:
14950  (void) index_reloptions(rel->rd_indam->amoptions, newOptions, true);
14951  break;
14952  default:
14953  ereport(ERROR,
14954  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
14955  errmsg("cannot set options for relation \"%s\"",
14957  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
14958  break;
14959  }
14960 
14961  /* Special-case validation of view options */
14962  if (rel->rd_rel->relkind == RELKIND_VIEW)
14963  {
14964  Query *view_query = get_view_query(rel);
14965  List *view_options = untransformRelOptions(newOptions);
14966  ListCell *cell;
14967  bool check_option = false;
14968 
14969  foreach(cell, view_options)
14970  {
14971  DefElem *defel = (DefElem *) lfirst(cell);
14972 
14973  if (strcmp(defel->defname, "check_option") == 0)
14974  check_option = true;
14975  }
14976 
14977  /*
14978  * If the check option is specified, look to see if the view is
14979  * actually auto-updatable or not.
14980  */
14981  if (check_option)
14982  {
14983  const char *view_updatable_error =
14984  view_query_is_auto_updatable(view_query, true);
14985 
14986  if (view_updatable_error)
14987  ereport(ERROR,
14988  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
14989  errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
14990  errhint("%s", _(view_updatable_error))));
14991  }
14992  }
14993 
14994  /*
14995  * All we need do here is update the pg_class row; the new options will be
14996  * propagated into relcaches during post-commit cache inval.
14997  */
14998  memset(repl_val, 0, sizeof(repl_val));
14999  memset(repl_null, false, sizeof(repl_null));
15000  memset(repl_repl, false, sizeof(repl_repl));
15001 
15002  if (newOptions != (Datum) 0)
15003  repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15004  else
15005  repl_null[Anum_pg_class_reloptions - 1] = true;
15006 
15007  repl_repl[Anum_pg_class_reloptions - 1] = true;
15008 
15009  newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15010  repl_val, repl_null, repl_repl);
15011 
15012  CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15013 
15014  InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15015 
15016  heap_freetuple(newtuple);
15017 
15018  ReleaseSysCache(tuple);
15019 
15020  /* repeat the whole exercise for the toast table, if there's one */
15021  if (OidIsValid(rel->rd_rel->reltoastrelid))
15022  {
15023  Relation toastrel;
15024  Oid toastid = rel->rd_rel->reltoastrelid;
15025 
15026  toastrel = table_open(toastid, lockmode);
15027 
15028  /* Fetch heap tuple */
15029  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
15030  if (!HeapTupleIsValid(tuple))
15031  elog(ERROR, "cache lookup failed for relation %u", toastid);
15032 
15033  if (operation == AT_ReplaceRelOptions)
15034  {
15035  /*
15036  * If we're supposed to replace the reloptions list, we just
15037  * pretend there were none before.
15038  */
15039  datum = (Datum) 0;
15040  isnull = true;
15041  }
15042  else
15043  {
15044  /* Get the old reloptions */
15045  datum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
15046  &isnull);
15047  }
15048 
15049  newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
15050  defList, "toast", validnsps, false,
15051  operation == AT_ResetRelOptions);
15052 
15053  (void) heap_reloptions(RELKIND_TOASTVALUE, newOptions, true);
15054 
15055  memset(repl_val, 0, sizeof(repl_val));
15056  memset(repl_null, false, sizeof(repl_null));
15057  memset(repl_repl, false, sizeof(repl_repl));
15058 
15059  if (newOptions != (Datum) 0)
15060  repl_val[Anum_pg_class_reloptions - 1] = newOptions;
15061  else
15062  repl_null[Anum_pg_class_reloptions - 1] = true;
15063 
15064  repl_repl[Anum_pg_class_reloptions - 1] = true;
15065 
15066  newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
15067  repl_val, repl_null, repl_repl);
15068 
15069  CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
15070 
15071  InvokeObjectPostAlterHookArg(RelationRelationId,
15072  RelationGetRelid(toastrel), 0,
15073  InvalidOid, true);
15074 
15075  heap_freetuple(newtuple);
15076 
15077  ReleaseSysCache(tuple);
15078 
15079  table_close(toastrel, NoLock);
15080  }
15081 
15082  table_close(pgclass, RowExclusiveLock);
15083 }
#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 16705 of file tablecmds.c.

16706 {
16707  Relation pg_class;
16708  Oid relid;
16709  HeapTuple tuple;
16710 
16711  relid = RelationGetRelid(rel);
16712 
16713  /* Pull the record for this relation and update it */
16714  pg_class = table_open(RelationRelationId, RowExclusiveLock);
16715 
16716  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relid));
16717 
16718  if (!HeapTupleIsValid(tuple))
16719  elog(ERROR, "cache lookup failed for relation %u", relid);
16720 
16721  ((Form_pg_class) GETSTRUCT(tuple))->relrowsecurity = rls;
16722  CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16723 
16724  InvokeObjectPostAlterHook(RelationRelationId,
16725  RelationGetRelid(rel), 0);
16726 
16727  table_close(pg_class, RowExclusiveLock);
16728  heap_freetuple(tuple);
16729 }

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

8586 {
8587  int newtarget = 0;
8588  bool newtarget_default;
8589  Relation attrelation;
8590  HeapTuple tuple,
8591  newtuple;
8592  Form_pg_attribute attrtuple;
8594  ObjectAddress address;
8595  Datum repl_val[Natts_pg_attribute];
8596  bool repl_null[Natts_pg_attribute];
8597  bool repl_repl[Natts_pg_attribute];
8598 
8599  /*
8600  * We allow referencing columns by numbers only for indexes, since table
8601  * column numbers could contain gaps if columns are later dropped.
8602  */
8603  if (rel->rd_rel->relkind != RELKIND_INDEX &&
8604  rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
8605  !colName)
8606  ereport(ERROR,
8607  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8608  errmsg("cannot refer to non-index column by number")));
8609 
8610  /* -1 was used in previous versions for the default setting */
8611  if (newValue && intVal(newValue) != -1)
8612  {
8613  newtarget = intVal(newValue);
8614  newtarget_default = false;
8615  }
8616  else
8617  newtarget_default = true;
8618 
8619  if (!newtarget_default)
8620  {
8621  /*
8622  * Limit target to a sane range
8623  */
8624  if (newtarget < 0)
8625  {
8626  ereport(ERROR,
8627  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8628  errmsg("statistics target %d is too low",
8629  newtarget)));
8630  }
8631  else if (newtarget > MAX_STATISTICS_TARGET)
8632  {
8633  newtarget = MAX_STATISTICS_TARGET;
8634  ereport(WARNING,
8635  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
8636  errmsg("lowering statistics target to %d",
8637  newtarget)));
8638  }
8639  }
8640 
8641  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8642 
8643  if (colName)
8644  {
8645  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
8646 
8647  if (!HeapTupleIsValid(tuple))
8648  ereport(ERROR,
8649  (errcode(ERRCODE_UNDEFINED_COLUMN),
8650  errmsg("column \"%s\" of relation \"%s\" does not exist",
8651  colName, RelationGetRelationName(rel))));
8652  }
8653  else
8654  {
8655  tuple = SearchSysCacheAttNum(RelationGetRelid(rel), colNum);
8656 
8657  if (!HeapTupleIsValid(tuple))
8658  ereport(ERROR,
8659  (errcode(ERRCODE_UNDEFINED_COLUMN),
8660  errmsg("column number %d of relation \"%s\" does not exist",
8661  colNum, RelationGetRelationName(rel))));
8662  }
8663 
8664  attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8665 
8666  attnum = attrtuple->attnum;
8667  if (attnum <= 0)
8668  ereport(ERROR,
8669  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8670  errmsg("cannot alter system column \"%s\"",
8671  colName)));
8672 
8673  if (rel->rd_rel->relkind == RELKIND_INDEX ||
8674  rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
8675  {
8676  if (attnum > rel->rd_index->indnkeyatts)
8677  ereport(ERROR,
8678  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8679  errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
8680  NameStr(attrtuple->attname), RelationGetRelationName(rel))));
8681  else if (rel->rd_index->indkey.values[attnum - 1] != 0)
8682  ereport(ERROR,
8683  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8684  errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
8685  NameStr(attrtuple->attname), RelationGetRelationName(rel)),
8686  errhint("Alter statistics on table column instead.")));
8687  }
8688 
8689  /* Build new tuple. */
8690  memset(repl_null, false, sizeof(repl_null));
8691  memset(repl_repl, false, sizeof(repl_repl));
8692  if (!newtarget_default)
8693  repl_val[Anum_pg_attribute_attstattarget - 1] = newtarget;
8694  else
8695  repl_null[Anum_pg_attribute_attstattarget - 1] = true;
8696  repl_repl[Anum_pg_attribute_attstattarget - 1] = true;
8697  newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrelation),
8698  repl_val, repl_null, repl_repl);
8699  CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
8700 
8701  InvokeObjectPostAlterHook(RelationRelationId,
8702  RelationGetRelid(rel),
8703  attrtuple->attnum);
8704  ObjectAddressSubSet(address, RelationRelationId,
8705  RelationGetRelid(rel), attnum);
8706 
8707  heap_freetuple(newtuple);
8708 
8709  ReleaseSysCache(tuple);
8710 
8711  table_close(attrelation, RowExclusiveLock);
8712 
8713  return address;
8714 }
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 8862 of file tablecmds.c.

8863 {
8864  Relation attrelation;
8865  HeapTuple tuple;
8866  Form_pg_attribute attrtuple;
8868  ObjectAddress address;
8869 
8870  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
8871 
8872  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), colName);
8873 
8874  if (!HeapTupleIsValid(tuple))
8875  ereport(ERROR,
8876  (errcode(ERRCODE_UNDEFINED_COLUMN),
8877  errmsg("column \"%s\" of relation \"%s\" does not exist",
8878  colName, RelationGetRelationName(rel))));
8879  attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8880 
8881  attnum = attrtuple->attnum;
8882  if (attnum <= 0)
8883  ereport(ERROR,
8884  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8885  errmsg("cannot alter system column \"%s\"",
8886  colName)));
8887 
8888  attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
8889 
8890  CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8891 
8892  InvokeObjectPostAlterHook(RelationRelationId,
8893  RelationGetRelid(rel),
8894  attrtuple->attnum);
8895 
8896  /*
8897  * Apply the change to indexes as well (only for simple index columns,
8898  * matching behavior of index.c ConstructTupleDescriptor()).
8899  */
8900  SetIndexStorageProperties(rel, attrelation, attnum,
8901  true, attrtuple->attstorage,
8902  false, 0,
8903  lockmode);
8904 
8905  heap_freetuple(tuple);
8906 
8907  table_close(attrelation, RowExclusiveLock);
8908 
8909  ObjectAddressSubSet(address, RelationRelationId,
8910  RelationGetRelid(rel), attnum);
8911  return address;
8912 }
static char GetAttributeStorage(Oid atttypid, const char *storagemode)
Definition: tablecmds.c:20028

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

15091 {
15092  Relation rel;
15093  Oid reltoastrelid;
15094  RelFileNumber newrelfilenumber;
15095  RelFileLocator newrlocator;
15096  List *reltoastidxids = NIL;
15097  ListCell *lc;
15098 
15099  /*
15100  * Need lock here in case we are recursing to toast table or index
15101  */
15102  rel = relation_open(tableOid, lockmode);
15103 
15104  /* Check first if relation can be moved to new tablespace */
15105  if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15106  {
15107  InvokeObjectPostAlterHook(RelationRelationId,
15108  RelationGetRelid(rel), 0);
15109  relation_close(rel, NoLock);
15110  return;
15111  }
15112 
15113  reltoastrelid = rel->rd_rel->reltoastrelid;
15114  /* Fetch the list of indexes on toast relation if necessary */
15115  if (OidIsValid(reltoastrelid))
15116  {
15117  Relation toastRel = relation_open(reltoastrelid, lockmode);
15118 
15119  reltoastidxids = RelationGetIndexList(toastRel);
15120  relation_close(toastRel, lockmode);
15121  }
15122 
15123  /*
15124  * Relfilenumbers are not unique in databases across tablespaces, so we
15125  * need to allocate a new one in the new tablespace.
15126  */
15127  newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
15128  rel->rd_rel->relpersistence);
15129 
15130  /* Open old and new relation */
15131  newrlocator = rel->rd_locator;
15132  newrlocator.relNumber = newrelfilenumber;
15133  newrlocator.spcOid = newTableSpace;
15134 
15135  /* hand off to AM to actually create new rel storage and copy the data */
15136  if (rel->rd_rel->relkind == RELKIND_INDEX)
15137  {
15138  index_copy_data(rel, newrlocator);
15139  }
15140  else
15141  {
15142  Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
15143  table_relation_copy_data(rel, &newrlocator);
15144  }
15145 
15146  /*
15147  * Update the pg_class row.
15148  *
15149  * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
15150  * executed on pg_class or its indexes (the above copy wouldn't contain
15151  * the updated pg_class entry), but that's forbidden with
15152  * CheckRelationTableSpaceMove().
15153  */
15154  SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
15155 
15156  InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15157 
15159 
15160  relation_close(rel, NoLock);
15161 
15162  /* Make sure the reltablespace change is visible */
15164 
15165  /* Move associated toast relation and/or indexes, too */
15166  if (OidIsValid(reltoastrelid))
15167  ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
15168  foreach(lc, reltoastidxids)
15169  ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
15170 
15171  /* Clean up */
15172  list_free(reltoastidxids);
15173 }
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition: catalog.c:521
void RelationAssumeNewRelfilelocator(Relation relation)
Definition: relcache.c:3966
Oid RelFileNumber
Definition: relpath.h:25
RelFileNumber relNumber
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition: tableam.h:1651
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
Definition: tablecmds.c:15090
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition: tablecmds.c:3530
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition: tablecmds.c:3587
static void index_copy_data(Relation rel, RelFileLocator newrlocator)
Definition: tablecmds.c:15384

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

15184 {
15185  /*
15186  * Shouldn't be called on relations having storage; these are processed in
15187  * phase 3.
15188  */
15189  Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
15190 
15191  /* check if relation can be moved to its new tablespace */
15192  if (!CheckRelationTableSpaceMove(rel, newTableSpace))
15193  {
15194  InvokeObjectPostAlterHook(RelationRelationId,
15195  RelationGetRelid(rel),
15196  0);
15197  return;
15198  }
15199 
15200  /* Update can be done, so change reltablespace */
15201  SetRelationTableSpace(rel, newTableSpace, InvalidOid);
15202 
15203  InvokeObjectPostAlterHook(RelationRelationId, RelationGetRelid(rel), 0);
15204 
15205  /* Make sure the reltablespace change is visible */
15207 }

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

20421 {
20422  Relation splitRel;
20423  Oid splitRelOid;
20424  char relname[NAMEDATALEN];
20425  Oid namespaceId;
20426  ListCell *listptr,
20427  *listptr2;
20428  bool isSameName = false;
20429  char tmpRelName[NAMEDATALEN];
20430  List *newPartRels = NIL;
20431  ObjectAddress object;
20432  Oid defaultPartOid;
20433 
20434  defaultPartOid = get_default_oid_from_partdesc(RelationGetPartitionDesc(rel, true));
20435 
20436  /*
20437  * We are going to detach and remove this partition: need to use exclusive
20438  * lock for preventing DML-queries to the partition.
20439  */
20440  splitRel = table_openrv(cmd->name, AccessExclusiveLock);
20441 
20442  splitRelOid = RelationGetRelid(splitRel);
20443 
20444  /* Check descriptions of new partitions. */
20445  foreach(listptr, cmd->partlist)
20446  {
20447  Oid existing_relid;
20448  SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20449 
20451 
20452  /*
20453  * Look up the namespace in which we are supposed to create the
20454  * partition, check we have permission to create there, lock it
20455  * against concurrent drop, and mark stmt->relation as
20456  * RELPERSISTENCE_TEMP if a temporary namespace is selected.
20457  */
20458  sps->name->relpersistence = rel->rd_rel->relpersistence;
20459  namespaceId =
20461 
20462  /*
20463  * This would fail later on anyway if the relation already exists. But
20464  * by catching it here we can emit a nicer error message.
20465  */
20466  existing_relid = get_relname_relid(relname, namespaceId);
20467  if (existing_relid == splitRelOid && !isSameName)
20468  /* One new partition can have the same name as split partition. */
20469  isSameName = true;
20470  else if (existing_relid != InvalidOid)
20471  ereport(ERROR,
20472  (errcode(ERRCODE_DUPLICATE_TABLE),
20473  errmsg("relation \"%s\" already exists", relname)));
20474  }
20475 
20476  /* Detach split partition. */
20477  RemoveInheritance(splitRel, rel, false);
20478  /* Do the final part of detaching. */
20479  DetachPartitionFinalize(rel, splitRel, false, defaultPartOid);
20480 
20481  /*
20482  * If new partition has the same name as split partition then we should
20483  * rename split partition for reusing name.
20484  */
20485  if (isSameName)
20486  {
20487  /*
20488  * We must bump the command counter to make the split partition tuple
20489  * visible for renaming.
20490  */
20492  /* Rename partition. */
20493  sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
20494  RenameRelationInternal(splitRelOid, tmpRelName, false, false);
20495 
20496  /*
20497  * We must bump the command counter to make the split partition tuple
20498  * visible after renaming.
20499  */
20501  }
20502 
20503  /* Create new partitions (like split partition), without indexes. */
20504  foreach(listptr, cmd->partlist)
20505  {
20506  SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20507  Relation newPartRel;
20508 
20509  newPartRel = createPartitionTable(sps->name, rel, context);
20510  newPartRels = lappend(newPartRels, newPartRel);
20511  }
20512 
20513  /* Copy data from split partition to new partitions. */
20514  moveSplitTableRows(rel, splitRel, cmd->partlist, newPartRels, defaultPartOid);
20515  /* Keep the lock until commit. */
20516  table_close(splitRel, NoLock);
20517 
20518  /* Attach new partitions to partitioned table. */
20519  forboth(listptr, cmd->partlist, listptr2, newPartRels)
20520  {
20521  SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20522  Relation newPartRel = (Relation) lfirst(listptr2);
20523 
20524  /*
20525  * wqueue = NULL: verification for each cloned constraint is not
20526  * needed.
20527  */
20528  attachPartitionTable(NULL, rel, newPartRel, sps->bound);
20529  /* Keep the lock until commit. */
20530  table_close(newPartRel, NoLock);
20531  }
20532 
20533  /* Drop split partition. */
20534  object.classId = RelationRelationId;
20535  object.objectId = splitRelOid;
20536  object.objectSubId = 0;
20537  /* Probably DROP_CASCADE is not needed. */
20538  performDeletion(&object, DROP_RESTRICT, 0);
20539 }
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:20123

References AccessExclusiveLock, attachPartitionTable(), SinglePartitionSpec::bound, CommandCounterIncrement(), context, createPartitionTable(), DetachPartitionFinalize(), DROP_RESTRICT, ereport, errcode(), errmsg(), ERROR, forboth, get_default_oid_from_partdesc(), get_relname_relid(), InvalidOid, lappend(), lfirst, moveSplitTableRows(), MyProcPid, SinglePartitionSpec::name, PartitionCmd::name, NAMEDATALEN, NIL, NoLock, PartitionCmd::partlist, performDeletion(), RangeVarGetAndCheckCreationNamespace(), RelationData::rd_rel, RelationGetPartitionDesc(), RelationGetRelid, relname, RangeVar::relname, RangeVar::relpersistence, 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 11576 of file tablecmds.c.

11578 {
11579  Relation conrel;
11580  SysScanDesc scan;
11581  ScanKeyData skey[3];
11582  HeapTuple tuple;
11583  Form_pg_constraint con;
11584  ObjectAddress address;
11585 
11586  conrel = table_open(ConstraintRelationId, RowExclusiveLock);
11587 
11588  /*
11589  * Find and check the target constraint
11590  */
11591  ScanKeyInit(&skey[0],
11592  Anum_pg_constraint_conrelid,
11593  BTEqualStrategyNumber, F_OIDEQ,
11595  ScanKeyInit(&skey[1],
11596  Anum_pg_constraint_contypid,
11597  BTEqualStrategyNumber, F_OIDEQ,
11599  ScanKeyInit(&skey[2],
11600  Anum_pg_constraint_conname,
11601  BTEqualStrategyNumber, F_NAMEEQ,
11602  CStringGetDatum(constrName));
11603  scan = systable_beginscan(conrel, ConstraintRelidTypidNameIndexId,
11604  true, NULL, 3, skey);
11605 
11606  /* There can be at most one matching row */
11607  if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
11608  ereport(ERROR,
11609  (errcode(ERRCODE_UNDEFINED_OBJECT),
11610  errmsg("constraint \"%s\" of relation \"%s\" does not exist",
11611  constrName, RelationGetRelationName(rel))));
11612 
11613  con = (Form_pg_constraint) GETSTRUCT(tuple);
11614  if (con->contype != CONSTRAINT_FOREIGN &&
11615  con->contype != CONSTRAINT_CHECK)
11616  ereport(ERROR,
11617  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
11618  errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key or check constraint",
11619  constrName, RelationGetRelationName(rel))));
11620 
11621  if (!con->convalidated)
11622  {
11623  AlteredTableInfo *tab;
11624  HeapTuple copyTuple;
11625  Form_pg_constraint copy_con;
11626 
11627  if (con->contype == CONSTRAINT_FOREIGN)
11628  {
11629  NewConstraint *newcon;
11630  Constraint *fkconstraint;
11631 
11632  /* Queue validation for phase 3 */
11633  fkconstraint = makeNode(Constraint);
11634  /* for now this is all we need */
11635  fkconstraint->conname = constrName;
11636 
11637  newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11638  newcon->name = constrName;
11639  newcon->contype = CONSTR_FOREIGN;
11640  newcon->refrelid = con->confrelid;
11641  newcon->refindid = con->conindid;
11642  newcon->conid = con->oid;
11643  newcon->qual = (Node *) fkconstraint;
11644 
11645  /* Find or create work queue entry for this table */
11646  tab = ATGetQueueEntry(wqueue, rel);
11647  tab->constraints = lappend(tab->constraints, newcon);
11648 
11649  /*
11650  * We disallow creating invalid foreign keys to or from
11651  * partitioned tables, so ignoring the recursion bit is okay.
11652  */
11653  }
11654  else if (con->contype == CONSTRAINT_CHECK)
11655  {
11656  List *children = NIL;
11657  ListCell *child;
11658  NewConstraint *newcon;
11659  Datum val;
11660  char *conbin;
11661 
11662  /*
11663  * If we're recursing, the parent has already done this, so skip
11664  * it. Also, if the constraint is a NO INHERIT constraint, we
11665  * shouldn't try to look for it in the children.
11666  */
11667  if (!recursing && !con->connoinherit)
11668  children = find_all_inheritors(RelationGetRelid(rel),
11669  lockmode, NULL);
11670 
11671  /*
11672  * For CHECK constraints, we must ensure that we only mark the
11673  * constraint as validated on the parent if it's already validated
11674  * on the children.
11675  *
11676  * We recurse before validating on the parent, to reduce risk of
11677  * deadlocks.
11678  */
11679  foreach(child, children)
11680  {
11681  Oid childoid = lfirst_oid(child);
11682  Relation childrel;
11683 
11684  if (childoid == RelationGetRelid(rel))
11685  continue;
11686 
11687  /*
11688  * If we are told not to recurse, there had better not be any
11689  * child tables, because we can't mark the constraint on the
11690  * parent valid unless it is valid for all child tables.
11691  */
11692  if (!recurse)
11693  ereport(ERROR,
11694  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
11695  errmsg("constraint must be validated on child tables too")));
11696 
11697  /* find_all_inheritors already got lock */
11698  childrel = table_open(childoid, NoLock);
11699 
11700  ATExecValidateConstraint(wqueue, childrel, constrName, false,
11701  true, lockmode);
11702  table_close(childrel, NoLock);
11703  }
11704 
11705  /* Queue validation for phase 3 */
11706  newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
11707  newcon->name = constrName;
11708  newcon->contype = CONSTR_CHECK;
11709  newcon->refrelid = InvalidOid;
11710  newcon->refindid = InvalidOid;
11711  newcon->conid = con->oid;
11712 
11713  val = SysCacheGetAttrNotNull(CONSTROID, tuple,
11714  Anum_pg_constraint_conbin);
11715  conbin = TextDatumGetCString(val);
11716  newcon->qual = (Node *) stringToNode(conbin);
11717 
11718  /* Find or create work queue entry for this table */
11719  tab = ATGetQueueEntry(wqueue, rel);
11720  tab->constraints = lappend(tab->constraints, newcon);
11721 
11722  /*
11723  * Invalidate relcache so that others see the new validated
11724  * constraint.
11725  */
11727  }
11728 
11729  /*
11730  * Now update the catalog, while we have the door open.
11731  */
11732  copyTuple = heap_copytuple(tuple);
11733  copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
11734  copy_con->convalidated = true;
11735  CatalogTupleUpdate(conrel, &copyTuple->t_self, copyTuple);
11736 
11737  InvokeObjectPostAlterHook(ConstraintRelationId, con->oid, 0);
11738 
11739  heap_freetuple(copyTuple);
11740 
11741  ObjectAddressSet(address, ConstraintRelationId, con->oid);
11742  }
11743  else
11744  address = InvalidObjectAddress; /* already validated */
11745 
11746  systable_endscan(scan);
11747 
11748  table_close(conrel, RowExclusiveLock);
11749 
11750  return address;
11751 }
#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 6361 of file tablecmds.c.

6362 {
6363  Oid relid = RelationGetRelid(rel);
6364  AlteredTableInfo *tab;
6365  ListCell *ltab;
6366 
6367  foreach(ltab, *wqueue)
6368  {
6369  tab = (AlteredTableInfo *) lfirst(ltab);
6370  if (tab->relid == relid)
6371  return tab;
6372  }
6373 
6374  /*
6375  * Not there, so add it. Note that we make a copy of the relation's
6376  * existing descriptor before anything interesting can happen to it.
6377  */
6378  tab = (AlteredTableInfo *) palloc0(sizeof(AlteredTableInfo));
6379  tab->relid = relid;
6380  tab->rel = NULL; /* set later */
6381  tab->relkind = rel->rd_rel->relkind;
6383  tab->newAccessMethod = InvalidOid;
6384  tab->chgAccessMethod = false;
6385  tab->newTableSpace = InvalidOid;
6386  tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
6387  tab->chgPersistence = false;
6388 
6389  *wqueue = lappend(*wqueue, tab);
6390 
6391  return tab;
6392 }
char newrelpersistence
Definition: tablecmds.c:195
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition: tupdesc.c:173

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

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

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

5567 {
5568  AlterTableCmd *newcmd = NULL;
5570  List *beforeStmts;
5571  List *afterStmts;
5572  ListCell *lc;
5573 
5574  /* Gin up an AlterTableStmt with just this subcommand and this table */
5575  atstmt->relation =
5578  -1);
5579  atstmt->relation->inh = recurse;
5580  atstmt->cmds = list_make1(cmd);
5581  atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5582  atstmt->missing_ok = false;
5583 
5584  /* Transform the AlterTableStmt */
5586  atstmt,
5587  context->queryString,
5588  &beforeStmts,
5589  &afterStmts);
5590 
5591  /* Execute any statements that should happen before these subcommand(s) */
5592  foreach(lc, beforeStmts)
5593  {
5594  Node *stmt = (Node *) lfirst(lc);
5595 
5598  }
5599 
5600  /* Examine the transformed subcommands and schedule them appropriately */
5601  foreach(lc, atstmt->cmds)
5602  {
5604  AlterTablePass pass;
5605 
5606  /*
5607  * This switch need only cover the subcommand types that can be added
5608  * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5609  * executing the subcommand immediately, as a substitute for the
5610  * original subcommand. (Note, however, that this does cause
5611  * AT_AddConstraint subcommands to be rescheduled into later passes,
5612  * which is important for index and foreign key constraints.)
5613  *
5614  * We assume we needn't do any phase-1 checks for added subcommands.
5615  */
5616  switch (cmd2->subtype)
5617  {
5618  case AT_SetNotNull:
5619  /* Need command-specific recursion decision */
5620  ATPrepSetNotNull(wqueue, rel, cmd2,
5621  recurse, false,
5622  lockmode, context);
5623  pass = AT_PASS_COL_ATTRS;
5624  break;
5625  case AT_AddIndex:
5626  /* This command never recurses */
5627  /* No command-specific prep needed */
5628  pass = AT_PASS_ADD_INDEX;
5629  break;
5630  case AT_AddIndexConstraint:
5631  /* This command never recurses */
5632  /* No command-specific prep needed */
5633  pass = AT_PASS_ADD_INDEXCONSTR;
5634  break;
5635  case AT_AddConstraint:
5636  /* Recursion occurs during execution phase */
5637  if (recurse)
5638  cmd2->recurse = true;
5639  switch (castNode(Constraint, cmd2->def)->contype)
5640  {
5641  case CONSTR_PRIMARY:
5642  case CONSTR_UNIQUE:
5643  case CONSTR_EXCLUSION:
5644  pass = AT_PASS_ADD_INDEXCONSTR;
5645  break;
5646  default:
5647  pass = AT_PASS_ADD_OTHERCONSTR;
5648  break;
5649  }
5650  break;
5652  /* This command never recurses */
5653  /* No command-specific prep needed */
5654  pass = AT_PASS_MISC;
5655  break;
5656  default:
5657  pass = cur_pass;
5658  break;
5659  }
5660 
5661  if (pass < cur_pass)
5662  {
5663  /* Cannot schedule into a pass we already finished */
5664  elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5665  pass);
5666  }
5667  else if (pass > cur_pass)
5668  {
5669  /* OK, queue it up for later */
5670  tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5671  }
5672  else
5673  {
5674  /*
5675  * We should see at most one subcommand for the current pass,
5676  * which is the transformed version of the original subcommand.
5677  */
5678  if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5679  {
5680  /* Found the transformed version of our subcommand */
5681  newcmd = cmd2;
5682  }
5683  else
5684  elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5685  pass);
5686  }
5687  }
5688 
5689  /* Queue up any after-statements to happen at the end */
5690  tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5691 
5692  return newcmd;
5693 }
AlterTableStmt * transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, const char *queryString, List **beforeStmts, List **afterStmts)
RangeVar * relation
Definition: parsenodes.h:2345
ObjectType objtype
Definition: parsenodes.h:2347
List * afterStmts
Definition: tablecmds.c:187
List * subcmds[AT_NUM_PASSES]
Definition: tablecmds.c:183
bool inh
Definition: primnodes.h:85
static void ATPrepSetNotNull(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:7666
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_SetNotNull, ATPrepSetNotNull(), 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 13701 of file tablecmds.c.

13702 {
13703  ObjectAddress obj;
13704  ObjectAddresses *objects;
13705  ListCell *def_item;
13706  ListCell *oid_item;
13707 
13708  /*
13709  * Collect all the constraints and indexes to drop so we can process them
13710  * in a single call. That way we don't have to worry about dependencies
13711  * among them.
13712  */
13713  objects = new_object_addresses();
13714 
13715  /*
13716  * Re-parse the index and constraint definitions, and attach them to the
13717  * appropriate work queue entries. We do this before dropping because in
13718  * the case of a FOREIGN KEY constraint, we might not yet have exclusive
13719  * lock on the table the constraint is attached to, and we need to get
13720  * that before reparsing/dropping.
13721  *
13722  * We can't rely on the output of deparsing to tell us which relation to
13723  * operate on, because concurrent activity might have made the name
13724  * resolve differently. Instead, we've got to use the OID of the
13725  * constraint or index we're processing to figure out which relation to
13726  * operate on.
13727  */
13728  forboth(oid_item, tab->changedConstraintOids,
13729  def_item, tab->changedConstraintDefs)
13730  {
13731  Oid oldId = lfirst_oid(oid_item);
13732  HeapTuple tup;
13733  Form_pg_constraint con;
13734  Oid relid;
13735  Oid confrelid;
13736  char contype;
13737  bool conislocal;
13738 
13739  tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
13740  if (!HeapTupleIsValid(tup)) /* should not happen */
13741  elog(ERROR, "cache lookup failed for constraint %u", oldId);
13742  con = (Form_pg_constraint) GETSTRUCT(tup);
13743  if (OidIsValid(con->conrelid))
13744  relid = con->conrelid;
13745  else
13746  {
13747  /* must be a domain constraint */
13748  relid = get_typ_typrelid(getBaseType(con->contypid));
13749  if (!OidIsValid(relid))
13750  elog(ERROR, "could not identify relation associated with constraint %u", oldId);
13751  }
13752  confrelid = con->confrelid;
13753  contype = con->contype;
13754  conislocal = con->conislocal;
13755  ReleaseSysCache(tup);
13756 
13757  ObjectAddressSet(obj, ConstraintRelationId, oldId);
13758  add_exact_object_address(&obj, objects);
13759 
13760  /*
13761  * If the constraint is inherited (only), we don't want to inject a
13762  * new definition here; it'll get recreated when ATAddCheckConstraint
13763  * recurses from adding the parent table's constraint. But we had to
13764  * carry the info this far so that we can drop the constraint below.
13765  */
13766  if (!conislocal)
13767  continue;
13768 
13769  /*
13770  * When rebuilding an FK constraint that references the table we're
13771  * modifying, we might not yet have any lock on the FK's table, so get
13772  * one now. We'll need AccessExclusiveLock for the DROP CONSTRAINT
13773  * step, so there's no value in asking for anything weaker.
13774  */
13775  if (relid != tab->relid && contype == CONSTRAINT_FOREIGN)
13777 
13778  ATPostAlterTypeParse(oldId, relid, confrelid,
13779  (char *) lfirst(def_item),
13780  wqueue, lockmode, tab->rewrite);
13781  }
13782  forboth(oid_item, tab->changedIndexOids,
13783  def_item, tab->changedIndexDefs)
13784  {
13785  Oid oldId = lfirst_oid(oid_item);
13786  Oid relid;
13787 
13788  relid = IndexGetRelation(oldId, false);
13789  ATPostAlterTypeParse(oldId, relid, InvalidOid,
13790  (char *) lfirst(def_item),
13791  wqueue, lockmode, tab->rewrite);
13792 
13793  ObjectAddressSet(obj, RelationRelationId, oldId);
13794  add_exact_object_address(&obj, objects);
13795  }
13796 
13797  /* add dependencies for new statistics */
13798  forboth(oid_item, tab->changedStatisticsOids,
13799  def_item, tab->changedStatisticsDefs)
13800  {
13801  Oid oldId = lfirst_oid(oid_item);
13802  Oid relid;
13803 
13804  relid = StatisticsGetRelation(oldId, false);
13805  ATPostAlterTypeParse(oldId, relid, InvalidOid,
13806  (char *) lfirst(def_item),
13807  wqueue, lockmode, tab->rewrite);
13808 
13809  ObjectAddressSet(obj, StatisticExtRelationId, oldId);
13810  add_exact_object_address(&obj, objects);
13811  }
13812 
13813  /*
13814  * Queue up command to restore replica identity index marking
13815  */
13816  if (tab->replicaIdentityIndex)
13817  {
13820 
13821  subcmd->identity_type = REPLICA_IDENTITY_INDEX;
13822  subcmd->name = tab->replicaIdentityIndex;
13823  cmd->subtype = AT_ReplicaIdentity;
13824  cmd->def = (Node *) subcmd;
13825 
13826  /* do it after indexes and constraints */
13827  tab->subcmds[AT_PASS_OLD_CONSTR] =
13828  lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13829  }
13830 
13831  /*
13832  * Queue up command to restore marking of index used for cluster.
13833  */
13834  if (tab->clusterOnIndex)
13835  {
13837 
13838  cmd->subtype = AT_ClusterOn;
13839  cmd->name = tab->clusterOnIndex;
13840 
13841  /* do it after indexes and constraints */
13842  tab->subcmds[AT_PASS_OLD_CONSTR] =
13843  lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
13844  }
13845 
13846  /*
13847  * It should be okay to use DROP_RESTRICT here, since nothing else should
13848  * be depending on these objects.
13849  */
13851 
13852  free_object_addresses(objects);
13853 
13854  /*
13855  * The objects will get recreated during subsequent passes over the work
13856  * queue.
13857  */
13858 }
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition: index.c:3524
Oid get_typ_typrelid(Oid typid)
Definition: lsyscache.c:2731
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition: statscmds.c:898
List * changedConstraintDefs
Definition: tablecmds.c:201
List * changedStatisticsDefs
Definition: tablecmds.c:207
char * clusterOnIndex
Definition: tablecmds.c:205
char * replicaIdentityIndex
Definition: tablecmds.c:204
List * changedStatisticsOids
Definition: tablecmds.c:206
List * changedIndexDefs
Definition: tablecmds.c:203
List * changedIndexOids
Definition: tablecmds.c:202
List * changedConstraintOids
Definition: tablecmds.c:200
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
Definition: tablecmds.c:13869

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

13871 {
13872  List *raw_parsetree_list;
13873  List *querytree_list;
13874  ListCell *list_item;
13875  Relation rel;
13876 
13877  /*
13878  * We expect that we will get only ALTER TABLE and CREATE INDEX
13879  * statements. Hence, there is no need to pass them through
13880  * parse_analyze_*() or the rewriter, but instead we need to pass them
13881  * through parse_utilcmd.c to make them ready for execution.
13882  */
13883  raw_parsetree_list = raw_parser(cmd, RAW_PARSE_DEFAULT);
13884  querytree_list = NIL;
13885  foreach(list_item, raw_parsetree_list)
13886  {
13887  RawStmt *rs = lfirst_node(RawStmt, list_item);
13888  Node *stmt = rs->stmt;
13889 
13890  if (IsA(stmt, IndexStmt))
13891  querytree_list = lappend(querytree_list,
13892  transformIndexStmt(oldRelId,
13893  (IndexStmt *) stmt,
13894  cmd));
13895  else if (IsA(stmt, AlterTableStmt))
13896  {
13897  List *beforeStmts;
13898  List *afterStmts;
13899 
13900  stmt = (Node *) transformAlterTableStmt(oldRelId,
13901  (AlterTableStmt *) stmt,
13902  cmd,
13903  &beforeStmts,
13904  &afterStmts);
13905  querytree_list = list_concat(querytree_list, beforeStmts);
13906  querytree_list = lappend(querytree_list, stmt);
13907  querytree_list = list_concat(querytree_list, afterStmts);
13908  }
13909  else if (IsA(stmt, CreateStatsStmt))
13910  querytree_list = lappend(querytree_list,
13911  transformStatsStmt(oldRelId,
13912  (CreateStatsStmt *) stmt,
13913  cmd));
13914  else
13915  querytree_list = lappend(querytree_list, stmt);
13916  }
13917 
13918  /* Caller should already have acquired whatever lock we need. */
13919  rel = relation_open(oldRelId, NoLock);
13920 
13921  /*
13922  * Attach each generated command to the proper place in the work queue.
13923  * Note this could result in creation of entirely new work-queue entries.
13924  *
13925  * Also note that we have to tweak the command subtypes, because it turns
13926  * out that re-creation of indexes and constraints has to act a bit
13927  * differently from initial creation.
13928  */
13929  foreach(list_item, querytree_list)
13930  {
13931  Node *stm = (Node *) lfirst(list_item);
13932  AlteredTableInfo *tab;
13933 
13934  tab = ATGetQueueEntry(wqueue, rel);
13935 
13936  if (IsA(stm, IndexStmt))
13937  {
13938  IndexStmt *stmt = (IndexStmt *) stm;
13939  AlterTableCmd *newcmd;
13940 
13941  if (!rewrite)
13942  TryReuseIndex(oldId, stmt);
13943  stmt->reset_default_tblspc = true;
13944  /* keep the index's comment */
13945  stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
13946 
13947  newcmd = makeNode(AlterTableCmd);
13948  newcmd->subtype = AT_ReAddIndex;
13949  newcmd->def = (Node *) stmt;
13950  tab->subcmds[AT_PASS_OLD_INDEX] =
13951  lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
13952  }
13953  else if (IsA(stm, AlterTableStmt))
13954  {
13955  AlterTableStmt *stmt = (AlterTableStmt *) stm;
13956  ListCell *lcmd;
13957 
13958  foreach(lcmd, stmt->cmds)
13959  {
13960  AlterTableCmd *cmd = lfirst_node(AlterTableCmd, lcmd);
13961 
13962  if (cmd->subtype == AT_AddIndex)
13963  {
13964  IndexStmt *indstmt;
13965  Oid indoid;
13966 
13967  indstmt = castNode(IndexStmt, cmd->def);
13968  indoid = get_constraint_index(oldId);
13969 
13970  if (!rewrite)
13971  TryReuseIndex(indoid, indstmt);
13972  /* keep any comment on the index */
13973  indstmt->idxcomment = GetComment(indoid,
13974  RelationRelationId, 0);
13975  indstmt->reset_default_tblspc = true;
13976 
13977  cmd->subtype = AT_ReAddIndex;
13978  tab->subcmds[AT_PASS_OLD_INDEX] =
13979  lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
13980 
13981  /* recreate any comment on the constraint */
13984  oldId,
13985  rel,
13986  NIL,
13987  indstmt->idxname);
13988  }
13989  else if (cmd->subtype == AT_AddConstraint)
13990  {
13991  Constraint *con = castNode(Constraint, cmd->def);
13992 
13993  con->old_pktable_oid = refRelId;
13994  /* rewriting neither side of a FK */
13995  if (con->contype == CONSTR_FOREIGN &&
13996  !rewrite && tab->rewrite == 0)
13997  TryReuseForeignKey(oldId, con);
13998  con->reset_default_tblspc = true;
13999  cmd->subtype = AT_ReAddConstraint;
14000  tab->subcmds[AT_PASS_OLD_CONSTR] =
14001  lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14002 
14003  /* recreate any comment on the constraint */
14006  oldId,
14007  rel,
14008  NIL,
14009  con->conname);
14010  }
14011  else if (cmd->subtype == AT_SetNotNull)
14012  {
14013  /*
14014  * The parser will create AT_SetNotNull subcommands for
14015  * columns of PRIMARY KEY indexes/constraints, but we need
14016  * not do anything with them here, because the columns'
14017  * NOT NULL marks will already have been propagated into
14018  * the new table definition.
14019  */
14020  }
14021  else
14022  elog(ERROR, "unexpected statement subtype: %d",
14023  (int) cmd->subtype);
14024  }
14025  }
14026  else if (IsA(stm, AlterDomainStmt))
14027  {
14029 
14030  if (stmt->subtype == 'C') /* ADD CONSTRAINT */
14031  {
14032  Constraint *con = castNode(Constraint, stmt->def);
14034 
14036  cmd->def = (Node *) stmt;
14037  tab->subcmds[AT_PASS_OLD_CONSTR] =
14038  lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
14039 
14040  /* recreate any comment on the constraint */
14043  oldId,
14044  NULL,
14045  stmt->typeName,
14046  con->conname);
14047  }
14048  else
14049  elog(ERROR, "unexpected statement subtype: %d",
14050  (int) stmt->subtype);
14051  }
14052  else if (IsA(stm, CreateStatsStmt))
14053  {
14055  AlterTableCmd *newcmd;
14056 
14057  /* keep the statistics object's comment */
14058  stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
14059 
14060  newcmd = makeNode(AlterTableCmd);
14061  newcmd->subtype = AT_ReAddStatistics;
14062  newcmd->def = (Node *) stmt;
14063  tab->subcmds[AT_PASS_MISC] =
14064  lappend(tab->subcmds[AT_PASS_MISC], newcmd);
14065  }
14066  else
14067  elog(ERROR, "unexpected statement type: %d",
14068  (int) nodeTag(stm));
14069  }
14070 
14071  relation_close(rel, NoLock);
14072 }
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:2760
bool reset_default_tblspc
Definition: parsenodes.h:3381
char * idxname
Definition: parsenodes.h:3356
char * idxcomment
Definition: parsenodes.h:3366
Node * stmt
Definition: parsenodes.h:2025
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
Definition: tablecmds.c:14129
static void TryReuseForeignKey(Oid oldId, Constraint *con)
Definition: tablecmds.c:14157
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
Definition: tablecmds.c:14085

References AT_AddConstraint, AT_AddIndex, AT_PASS_MISC, AT_PASS_OLD_CONSTR, AT_PASS_OLD_INDEX, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReAddIndex, AT_ReAddStatistics, AT_SetNotNull, 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 6996 of file tablecmds.c.

6999 {
7000  if (rel->rd_rel->reloftype && !recursing)
7001  ereport(ERROR,
7002  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
7003  errmsg("cannot add column to typed table")));
7004 
7005  if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7006  ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7007 
7008  if (recurse && !is_view)
7009  cmd->recurse = true;
7010 }
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6694

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

15477 {
15478  if (child_rel->rd_rel->reloftype)
15479  ereport(ERROR,
15480  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15481  errmsg("cannot change inheritance of typed table")));
15482 
15483  if (child_rel->rd_rel->relispartition)
15484  ereport(ERROR,
15485  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15486  errmsg("cannot change inheritance of a partition")));
15487 
15488  if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15489  ereport(ERROR,
15490  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
15491  errmsg("cannot change inheritance of partitioned table")));
15492 }

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

Referenced by ATPrepCmd().

◆ ATPrepAlterColumnType()

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

Definition at line 12679 of file tablecmds.c.

12684 {
12685  char *colName = cmd->name;
12686  ColumnDef *def = (ColumnDef *) cmd->def;
12687  TypeName *typeName = def->typeName;
12688  Node *transform = def->cooked_default;
12689  HeapTuple tuple;
12690  Form_pg_attribute attTup;
12692  Oid targettype;
12693  int32 targettypmod;
12694  Oid targetcollid;
12696  ParseState *pstate = make_parsestate(NULL);
12697  AclResult aclresult;
12698  bool is_expr;
12699 
12700  if (rel->rd_rel->reloftype && !recursing)
12701  ereport(ERROR,
12702  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12703  errmsg("cannot alter column type of typed table")));
12704 
12705  /* lookup the attribute so we can check inheritance status */
12706  tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
12707  if (!HeapTupleIsValid(tuple))
12708  ereport(ERROR,
12709  (errcode(ERRCODE_UNDEFINED_COLUMN),
12710  errmsg("column \"%s\" of relation \"%s\" does not exist",
12711  colName, RelationGetRelationName(rel))));
12712  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
12713  attnum = attTup->attnum;
12714 
12715  /* Can't alter a system attribute */
12716  if (attnum <= 0)
12717  ereport(ERROR,
12718  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12719  errmsg("cannot alter system column \"%s\"",
12720  colName)));
12721 
12722  /*
12723  * Don't alter inherited columns. At outer level, there had better not be
12724  * any inherited definition; when recursing, we assume this was checked at
12725  * the parent level (see below).
12726  */
12727  if (attTup->attinhcount > 0 && !recursing)
12728  ereport(ERROR,
12729  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12730  errmsg("cannot alter inherited column \"%s\"",
12731  colName)));
12732 
12733  /* Don't alter columns used in the partition key */
12734  if (has_partition_attrs(rel,
12736  &is_expr))
12737  ereport(ERROR,
12738  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12739  errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
12740  colName, RelationGetRelationName(rel))));
12741 
12742  /* Look up the target type */
12743  typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
12744 
12745  aclresult = object_aclcheck(TypeRelationId, targettype, GetUserId(), ACL_USAGE);
12746  if (aclresult != ACLCHECK_OK)
12747  aclcheck_error_type(aclresult, targettype);
12748 
12749  /* And the collation */
12750  targetcollid = GetColumnDefCollation(NULL, def, targettype);
12751 
12752  /* make sure datatype is legal for a column */
12753  CheckAttributeType(colName, targettype, targetcollid,
12754  list_make1_oid(rel->rd_rel->reltype),
12755  0);
12756 
12757  if (tab->relkind == RELKIND_RELATION ||
12758  tab->relkind == RELKIND_PARTITIONED_TABLE)
12759  {
12760  /*
12761  * Set up an expression to transform the old data value to the new
12762  * type. If a USING option was given, use the expression as
12763  * transformed by transformAlterTableStmt, else just take the old
12764  * value and try to coerce it. We do this first so that type
12765  * incompatibility can be detected before we waste effort, and because
12766  * we need the expression to be parsed against the original table row
12767  * type.
12768  */
12769  if (!transform)
12770  {
12771  transform = (Node *) makeVar(1, attnum,
12772  attTup->atttypid, attTup->atttypmod,
12773  attTup->attcollation,
12774  0);
12775  }
12776 
12777  transform = coerce_to_target_type(pstate,
12778  transform, exprType(transform),
12779  targettype, targettypmod,
12782  -1);
12783  if (transform == NULL)
12784  {
12785  /* error text depends on whether USING was specified or not */
12786  if (def->cooked_default != NULL)
12787  ereport(ERROR,
12788  (errcode(ERRCODE_DATATYPE_MISMATCH),
12789  errmsg("result of USING clause for column \"%s\""
12790  " cannot be cast automatically to type %s",
12791  colName, format_type_be(targettype)),
12792  errhint("You might need to add an explicit cast.")));
12793  else
12794  ereport(ERROR,
12795  (errcode(ERRCODE_DATATYPE_MISMATCH),
12796  errmsg("column \"%s\" cannot be cast automatically to type %s",
12797  colName, format_type_be(targettype)),
12798  /* translator: USING is SQL, don't translate it */
12799  errhint("You might need to specify \"USING %s::%s\".",
12800  quote_identifier(colName),
12801  format_type_with_typemod(targettype,
12802  targettypmod))));
12803  }
12804 
12805  /* Fix collations after all else */
12806  assign_expr_collations(pstate, transform);
12807 
12808  /* Plan the expr now so we can accurately assess the need to rewrite. */
12809  transform = (Node *) expression_planner((Expr *) transform);
12810 
12811  /*
12812  * Add a work queue item to make ATRewriteTable update the column
12813  * contents.
12814  */
12816  newval->attnum = attnum;
12817  newval->expr = (Expr *) transform;
12818  newval->is_generated = false;
12819 
12820  tab->newvals = lappend(tab->newvals, newval);
12821  if (ATColumnChangeRequiresRewrite(transform, attnum))
12823  }
12824  else if (transform)
12825  ereport(ERROR,
12826  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
12827  errmsg("\"%s\" is not a table",
12828  RelationGetRelationName(rel))));
12829 
12830  if (!RELKIND_HAS_STORAGE(tab->relkind))
12831  {
12832  /*
12833  * For relations without storage, do this check now. Regular tables
12834  * will check it later when the table is being rewritten.
12835  */
12836  find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
12837  }
12838 
12839  ReleaseSysCache(tuple);
12840 
12841  /*
12842  * Recurse manually by queueing a new command for each child, if
12843  * necessary. We cannot apply ATSimpleRecursion here because we need to
12844  * remap attribute numbers in the USING expression, if any.
12845  *
12846  * If we are told not to recurse, there had better not be any child
12847  * tables; else the alter would put them out of step.
12848  */
12849  if (recurse)
12850  {
12851  Oid relid = RelationGetRelid(rel);
12852  List *child_oids,
12853  *child_numparents;
12854  ListCell *lo,
12855  *li;
12856 
12857  child_oids = find_all_inheritors(relid, lockmode,
12858  &child_numparents);
12859 
12860  /*
12861  * find_all_inheritors does the recursive search of the inheritance
12862  * hierarchy, so all we have to do is process all of the relids in the
12863  * list that it returns.
12864  */
12865  forboth(lo, child_oids, li, child_numparents)
12866  {
12867  Oid childrelid = lfirst_oid(lo);
12868  int numparents = lfirst_int(li);
12869  Relation childrel;
12870  HeapTuple childtuple;
12871  Form_pg_attribute childattTup;
12872 
12873  if (childrelid == relid)
12874  continue;
12875 
12876  /* find_all_inheritors already got lock */
12877  childrel = relation_open(childrelid, NoLock);
12878  CheckAlterTableIsSafe(childrel);
12879 
12880  /*
12881  * Verify that the child doesn't have any inherited definitions of
12882  * this column that came from outside this inheritance hierarchy.
12883  * (renameatt makes a similar test, though in a different way
12884  * because of its different recursion mechanism.)
12885  */
12886  childtuple = SearchSysCacheAttName(RelationGetRelid(childrel),
12887  colName);
12888  if (!HeapTupleIsValid(childtuple))
12889  ereport(ERROR,
12890  (errcode(ERRCODE_UNDEFINED_COLUMN),
12891  errmsg("column \"%s\" of relation \"%s\" does not exist",
12892  colName, RelationGetRelationName(childrel))));
12893  childattTup = (Form_pg_attribute) GETSTRUCT(childtuple);
12894 
12895  if (childattTup->attinhcount > numparents)
12896  ereport(ERROR,
12897  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12898  errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
12899  colName, RelationGetRelationName(childrel))));
12900 
12901  ReleaseSysCache(childtuple);
12902 
12903  /*
12904  * Remap the attribute numbers. If no USING expression was
12905  * specified, there is no need for this step.
12906  */
12907  if (def->cooked_default)
12908  {
12909  AttrMap *attmap;
12910  bool found_whole_row;
12911 
12912  /* create a copy to scribble on */
12913  cmd = copyObject(cmd);
12914 
12915  attmap = build_attrmap_by_name(RelationGetDescr(childrel),
12916  RelationGetDescr(rel),
12917  false);
12918  ((ColumnDef *) cmd->def)->cooked_default =
12920  1, 0,
12921  attmap,
12922  InvalidOid, &found_whole_row);
12923  if (found_whole_row)
12924  ereport(ERROR,
12925  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
12926  errmsg("cannot convert whole-row table reference"),
12927  errdetail("USING expression contains a whole-row table reference.")));
12928  pfree(attmap);
12929  }
12930  ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
12931  relation_close(childrel, NoLock);
12932  }
12933  }
12934  else if (!recursing &&
12936  ereport(ERROR,
12937  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
12938  errmsg("type of inherited column \"%s\" must be changed in child tables too",
12939  colName)));
12940 
12941  if (tab->relkind == RELKIND_COMPOSITE_TYPE)
12942  ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
12943 }
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition: aclchk.c:3019
#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:12596
Node * cooked_default
Definition: parsenodes.h:736
static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno)
Definition: tablecmds.c:12960
void find_composite_type_dependencies(Oid typeOid, Relation origRelation, const char *origTypeName)
Definition: tablecmds.c:6739

References ACL_USAGE, aclcheck_error_type(), ACLCHECK_OK, assign_expr_collations(), AT_REWRITE_COLUMN_REWRITE, ATColumnChangeRequiresRewrite(), ATPrepCmd(), attnum, ATTypedTableRecursion(), bms_make_singleton(), build_attrmap_by_name(), CheckAlterTableIsSafe(), CheckAttributeType(), 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 16925 of file tablecmds.c.

16926 {
16927  Relation pg_constraint;
16928  HeapTuple tuple;
16929  SysScanDesc scan;
16930  ScanKeyData skey[1];
16931 
16932  /*
16933  * Disallow changing status for a temp table. Also verify whether we can
16934  * get away with doing nothing; in such cases we don't need to run the
16935  * checks below, either.
16936  */
16937  switch (rel->rd_rel->relpersistence)
16938  {
16939  case RELPERSISTENCE_TEMP:
16940  ereport(ERROR,
16941  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
16942  errmsg("cannot change logged status of table \"%s\" because it is temporary",
16944  errtable(rel)));
16945  break;
16946  case RELPERSISTENCE_PERMANENT:
16947  if (toLogged)
16948  /* nothing to do */
16949  return false;
16950  break;
16951  case RELPERSISTENCE_UNLOGGED:
16952  if (!toLogged)
16953  /* nothing to do */
16954  return false;
16955  break;
16956  }
16957 
16958  /*
16959  * Check that the table is not part of any publication when changing to
16960  * UNLOGGED, as UNLOGGED tables can't be published.
16961  */
16962  if (!toLogged &&
16964  ereport(ERROR,
16965  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16966  errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
16968  errdetail("Unlogged relations cannot be replicated.")));
16969 
16970  /*
16971  * Check existing foreign key constraints to preserve the invariant that
16972  * permanent tables cannot reference unlogged ones. Self-referencing
16973  * foreign keys can safely be ignored.
16974  */
16975  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
16976 
16977  /*
16978  * Scan conrelid if changing to permanent, else confrelid. This also
16979  * determines whether a useful index exists.
16980  */
16981  ScanKeyInit(&skey[0],
16982  toLogged ? Anum_pg_constraint_conrelid :
16983  Anum_pg_constraint_confrelid,
16984  BTEqualStrategyNumber, F_OIDEQ,
16986  scan = systable_beginscan(pg_constraint,
16987  toLogged ? ConstraintRelidTypidNameIndexId : InvalidOid,
16988  true, NULL, 1, skey);
16989 
16990  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
16991  {
16993 
16994  if (con->contype == CONSTRAINT_FOREIGN)
16995  {
16996  Oid foreignrelid;
16997  Relation foreignrel;
16998 
16999  /* the opposite end of what we used as scankey */
17000  foreignrelid = toLogged ? con->confrelid : con->conrelid;
17001 
17002  /* ignore if self-referencing */
17003  if (RelationGetRelid(rel) == foreignrelid)
17004  continue;
17005 
17006  foreignrel = relation_open(foreignrelid, AccessShareLock);
17007 
17008  if (toLogged)
17009  {
17010  if (!RelationIsPermanent(foreignrel))
17011  ereport(ERROR,
17012  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17013  errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
17015  RelationGetRelationName(foreignrel)),
17016  errtableconstraint(rel, NameStr(con->conname))));
17017  }
17018  else
17019  {
17020  if (RelationIsPermanent(foreignrel))
17021  ereport(ERROR,
17022  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
17023  errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
17025  RelationGetRelationName(foreignrel)),
17026  errtableconstraint(rel, NameStr(con->conname))));
17027  }
17028 
17029  relation_close(foreignrel, AccessShareLock);
17030  }
17031  }
17032 
17033  systable_endscan(scan);
17034 
17035  table_close(pg_constraint, AccessShareLock);
17036 
17037  return true;
17038 }
List * GetRelationPublications(Oid relid)
int errtableconstraint(Relation rel, const char *conname)
Definition: relcache.c:6006
int errtable(Relation rel)
Definition: relcache.c:5952

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

4753 {
4754  AlteredTableInfo *tab;
4756 
4757  /* Find or create work queue entry for this table */
4758  tab = ATGetQueueEntry(wqueue, rel);
4759 
4760  /*
4761  * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4762  * partitions that are pending detach.
4763  */
4764  if (rel->rd_rel->relispartition &&
4767  ereport(ERROR,
4768  errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
4769  errmsg("cannot alter partition \"%s\" with an incomplete detach",
4771  errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4772 
4773  /*
4774  * Copy the original subcommand for each table, so we can scribble on it.
4775  * This avoids conflicts when different child tables need to make
4776  * different parse transformations (for example, the same column may have
4777  * different column numbers in different children).
4778  */
4779  cmd = copyObject(cmd);
4780 
4781  /*
4782  * Do permissions and relkind checking, recursion to child tables if
4783  * needed, and any additional phase-1 processing needed. (But beware of
4784  * adding any processing that looks at table details that another
4785  * subcommand could change. In some cases we reject multiple subcommands
4786  * that could try to change the same state in contrary ways.)
4787  */
4788  switch (cmd->subtype)
4789  {
4790  case AT_AddColumn: /* ADD COLUMN */
4791  ATSimplePermissions(cmd->subtype, rel,
4793  ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4794  lockmode, context);
4795  /* Recursion occurs during execution phase */
4796  pass = AT_PASS_ADD_COL;
4797  break;
4798  case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4799  ATSimplePermissions(cmd->subtype, rel, ATT_VIEW);
4800  ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4801  lockmode, context);
4802  /* Recursion occurs during execution phase */
4803  pass = AT_PASS_ADD_COL;
4804  break;
4805  case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4806 
4807  /*
4808  * We allow defaults on views so that INSERT into a view can have
4809  * default-ish behavior. This works because the rewriter
4810  * substitutes default values into INSERTs before it expands
4811  * rules.
4812  */
4814  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4815  /* No command-specific prep needed */
4816  pass = cmd->def ? AT_PASS_ADD_OTHERCONSTR : AT_PASS_DROP;
4817  break;
4818  case AT_CookedColumnDefault: /* add a pre-cooked default */
4819  /* This is currently used only in CREATE TABLE */
4820  /* (so the permission check really isn't necessary) */
4822  /* This command never recurses */
4823  pass = AT_PASS_ADD_OTHERCONSTR;
4824  break;
4825  case AT_AddIdentity:
4827  /* Set up recursion for phase 2; no other prep needed */
4828  if (recurse)
4829  cmd->recurse = true;
4830  pass = AT_PASS_ADD_OTHERCONSTR;
4831  break;
4832  case AT_SetIdentity:
4834  /* Set up recursion for phase 2; no other prep needed */
4835  if (recurse)
4836  cmd->recurse = true;
4837  /* This should run after AddIdentity, so do it in MISC pass */
4838  pass = AT_PASS_MISC;
4839  break;
4840  case AT_DropIdentity:
4842  /* Set up recursion for phase 2; no other prep needed */
4843  if (recurse)
4844  cmd->recurse = true;
4845  pass = AT_PASS_DROP;
4846  break;
4847  case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
4849  ATPrepDropNotNull(rel, recurse, recursing);
4850  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4851  pass = AT_PASS_DROP;
4852  break;
4853  case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
4855  /* Need command-specific recursion decision */
4856  ATPrepSetNotNull(wqueue, rel, cmd, recurse, recursing,
4857  lockmode, context);
4858  pass = AT_PASS_COL_ATTRS;
4859  break;
4860  case AT_CheckNotNull: /* check column is already marked NOT NULL */
4862  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4863  /* No command-specific prep needed */
4864  pass = AT_PASS_COL_ATTRS;
4865  break;
4866  case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
4868  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4869  pass = AT_PASS_SET_EXPRESSION;
4870  break;
4871  case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
4873  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4874  ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
4875  pass = AT_PASS_DROP;
4876  break;
4877  case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
4879  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4880  /* No command-specific prep needed */
4881  pass = AT_PASS_MISC;
4882  break;
4883  case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
4884  case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
4886  /* This command never recurses */
4887  pass = AT_PASS_MISC;
4888  break;
4889  case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
4891  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4892  /* No command-specific prep needed */
4893  pass = AT_PASS_MISC;
4894  break;
4895  case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
4897  /* This command never recurses */
4898  /* No command-specific prep needed */
4899  pass = AT_PASS_MISC;
4900  break;
4901  case AT_DropColumn: /* DROP COLUMN */
4902  ATSimplePermissions(cmd->subtype, rel,
4904  ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
4905  lockmode, context);
4906  /* Recursion occurs during execution phase */
4907  pass = AT_PASS_DROP;
4908  break;
4909  case AT_AddIndex: /* ADD INDEX */
4911  /* This command never recurses */
4912  /* No command-specific prep needed */
4913  pass = AT_PASS_ADD_INDEX;
4914  break;
4915  case AT_AddConstraint: /* ADD CONSTRAINT */
4917  /* Recursion occurs during execution phase */
4918  /* No command-specific prep needed except saving recurse flag */
4919  if (recurse)
4920  cmd->recurse = true;
4921  pass = AT_PASS_ADD_CONSTR;
4922  break;
4923  case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
4925  /* This command never recurses */
4926  /* No command-specific prep needed */
4927  pass = AT_PASS_ADD_INDEXCONSTR;
4928  break;
4929  case AT_DropConstraint: /* DROP CONSTRAINT */
4931  ATCheckPartitionsNotInUse(rel, lockmode);
4932  /* Other recursion occurs during execution phase */
4933  /* No command-specific prep needed except saving recurse flag */
4934  if (recurse)
4935  cmd->recurse = true;
4936  pass = AT_PASS_DROP;
4937  break;
4938  case AT_AlterColumnType: /* ALTER COLUMN TYPE */
4939  ATSimplePermissions(cmd->subtype, rel,
4941  /* See comments for ATPrepAlterColumnType */
4942  cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
4944  Assert(cmd != NULL);
4945  /* Performs own recursion */
4946  ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
4947  lockmode, context);
4948  pass = AT_PASS_ALTER_TYPE;
4949  break;
4952  /* This command never recurses */
4953  /* No command-specific prep needed */
4954  pass = AT_PASS_MISC;
4955  break;
4956  case AT_ChangeOwner: /* ALTER OWNER */
4957  /* This command never recurses */
4958  /* No command-specific prep needed */
4959  pass = AT_PASS_MISC;
4960  break;
4961  case AT_ClusterOn: /* CLUSTER ON */
4962  case AT_DropCluster: /* SET WITHOUT CLUSTER */
4964  /* These commands never recurse */
4965  /* No command-specific prep needed */
4966  pass = AT_PASS_MISC;
4967  break;
4968  case AT_SetLogged: /* SET LOGGED */
4970  if (tab->chgPersistence)
4971  ereport(ERROR,
4972  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4973  errmsg("cannot change persistence setting twice")));
4974  tab->chgPersistence = ATPrepChangePersistence(rel, true);
4975  /* force rewrite if necessary; see comment in ATRewriteTables */
4976  if (tab->chgPersistence)
4977  {
4979  tab->newrelpersistence = RELPERSISTENCE_PERMANENT;
4980  }
4981  pass = AT_PASS_MISC;
4982  break;
4983  case AT_SetUnLogged: /* SET UNLOGGED */
4985  if (tab->chgPersistence)
4986  ereport(ERROR,
4987  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4988  errmsg("cannot change persistence setting twice")));
4989  tab->chgPersistence = ATPrepChangePersistence(rel, false);
4990  /* force rewrite if necessary; see comment in ATRewriteTables */
4991  if (tab->chgPersistence)
4992  {
4994  tab->newrelpersistence = RELPERSISTENCE_UNLOGGED;
4995  }
4996  pass = AT_PASS_MISC;
4997  break;
4998  case AT_DropOids: /* SET WITHOUT OIDS */
5000  pass = AT_PASS_DROP;
5001  break;
5002  case AT_SetAccessMethod: /* SET ACCESS METHOD */
5004 
5005  /* check if another access method change was already requested */
5006  if (tab->chgAccessMethod)
5007  ereport(ERROR,
5008  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
5009  errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5010 
5011  ATPrepSetAccessMethod(tab, rel, cmd->name);
5012  pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5013  break;
5014  case AT_SetTableSpace: /* SET TABLESPACE */
5017  /* This command never recurses */
5018  ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5019  pass = AT_PASS_MISC; /* doesn't actually matter */
5020  break;
5021  case AT_SetRelOptions: /* SET (...) */
5022  case AT_ResetRelOptions: /* RESET (...) */
5023  case AT_ReplaceRelOptions: /* reset them all, then set just these */
5025  /* This command never recurses */
5026  /* No command-specific prep needed */
5027  pass = AT_PASS_MISC;
5028  break;
5029  case AT_AddInherit: /* INHERIT */
5031  /* This command never recurses */
5032  ATPrepAddInherit(rel);
5033  pass = AT_PASS_MISC;
5034  break;
5035  case AT_DropInherit: /* NO INHERIT */
5037  /* This command never recurses */
5038  /* No command-specific prep needed */
5039  pass = AT_PASS_MISC;
5040  break;
5041  case AT_AlterConstraint: /* ALTER CONSTRAINT */
5043  /* Recursion occurs during execution phase */
5044  pass = AT_PASS_MISC;
5045  break;
5046  case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5048  /* Recursion occurs during execution phase */
5049  /* No command-specific prep needed except saving recurse flag */
5050  if (recurse)
5051  cmd->recurse = true;
5052  pass = AT_PASS_MISC;
5053  break;
5054  case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5056  pass = AT_PASS_MISC;
5057  /* This command never recurses */
5058  /* No command-specific prep needed */
5059  break;
5060  case AT_EnableTrig: /* ENABLE TRIGGER variants */
5061  case AT_EnableAlwaysTrig:
5062  case AT_EnableReplicaTrig:
5063  case AT_EnableTrigAll:
5064  case AT_EnableTrigUser:
5065  case AT_DisableTrig: /* DISABLE TRIGGER variants */
5066  case AT_DisableTrigAll:
5067  case AT_DisableTrigUser:
5069  /* Set up recursion for phase 2; no other prep needed */
5070  if (recurse)
5071  cmd->recurse = true;
5072  pass = AT_PASS_MISC;
5073  break;
5074  case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5075  case AT_EnableAlwaysRule:
5076  case AT_EnableReplicaRule:
5077  case AT_DisableRule:
5078  case AT_AddOf: /* OF */
5079  case AT_DropOf: /* NOT OF */
5080  case AT_EnableRowSecurity:
5081  case AT_DisableRowSecurity:
5082  case AT_ForceRowSecurity:
5083  case AT_NoForceRowSecurity:
5085  /* These commands never recurse */
5086  /* No command-specific prep needed */
5087  pass = AT_PASS_MISC;
5088  break;
5089  case AT_GenericOptions:
5091  /* No command-specific prep needed */
5092  pass = AT_PASS_MISC;
5093  break;
5094  case AT_AttachPartition:
5096  /* No command-specific prep needed */
5097  pass = AT_PASS_MISC;
5098  break;
5099  case AT_DetachPartition:
5101  /* No command-specific prep needed */
5102  pass = AT_PASS_MISC;
5103  break;
5106  /* No command-specific prep needed */
5107  pass = AT_PASS_MISC;
5108  break;
5109  case AT_SplitPartition:
5111  /* No command-specific prep needed */
5112  pass = AT_PASS_MISC;
5113  break;
5114  case AT_MergePartitions:
5116  /* No command-specific prep needed */
5117  pass = AT_PASS_MISC;
5118  break;
5119  default: /* oops */
5120  elog(ERROR, "unrecognized alter table type: %d",
5121  (int) cmd->subtype);
5122  pass = AT_PASS_UNSET; /* keep compiler quiet */
5123  break;
5124  }
5125  Assert(pass > AT_PASS_UNSET);
5126 
5127  /* Add the subcommand to the appropriate list for phase 2 */
5128  tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5129 }
#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:332
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
Definition: tablecmds.c:14857
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition: tablecmds.c:6664
static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing)
Definition: tablecmds.c:7507
#define ATT_INDEX
Definition: tablecmds.c:328
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition: tablecmds.c:8448
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:8925
static bool ATPrepChangePersistence(Relation rel, bool toLogged)
Definition: tablecmds.c:16925
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6996
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:6619
#define ATT_PARTITIONED_INDEX
Definition: tablecmds.c:331
#define ATT_VIEW
Definition: tablecmds.c:326
static void ATPrepAddInherit(Relation child_rel)
Definition: tablecmds.c:15476
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition: tablecmds.c:12679
#define ATT_COMPOSITE_TYPE
Definition: tablecmds.c:329
#define ATT_MATVIEW
Definition: tablecmds.c:327
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)
Definition: tablecmds.c:14733

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_CheckNotNull, 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_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(), ATPrepDropNotNull(), ATPrepSetAccessMethod(), ATPrepSetNotNull(), 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(), 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 8925 of file tablecmds.c.

8928 {
8929  if (rel->rd_rel->reloftype && !recursing)
8930  ereport(ERROR,
8931  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
8932  errmsg("cannot drop column from typed table")));
8933 
8934  if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
8935  ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
8936 
8937  if (recurse)
8938  cmd->recurse = true;
8939 }

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

8449 {
8450  /*
8451  * Reject ONLY if there are child tables. We could implement this, but it
8452  * is a bit complicated. GENERATED clauses must be attached to the column
8453  * definition and cannot be added later like DEFAULT, so if a child table
8454  * has a generation expression that the parent does not have, the child
8455  * column will necessarily be an attislocal column. So to implement ONLY
8456  * here, we'd need extra code to update attislocal of the direct child
8457  * tables, somewhat similar to how DROP COLUMN does it, so that the
8458  * resulting state can be properly dumped and restored.
8459  */
8460  if (!recurse &&
8462  ereport(ERROR,
8463  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
8464  errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8465 
8466  /*
8467  * Cannot drop generation expression from inherited columns.
8468  */
8469  if (!recursing)
8470  {
8471  HeapTuple tuple;
8472  Form_pg_attribute attTup;
8473 
8474  tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), cmd->name);
8475  if (!HeapTupleIsValid(tuple))
8476  ereport(ERROR,
8477  (errcode(ERRCODE_UNDEFINED_COLUMN),
8478  errmsg("column \"%s\" of relation \"%s\" does not exist",
8479  cmd->name, RelationGetRelationName(rel))));
8480 
8481  attTup = (Form_pg_attribute) GETSTRUCT(tuple);
8482 
8483  if (attTup->attinhcount > 0)
8484  ereport(ERROR,
8485  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
8486  errmsg("cannot drop generation expression from inherited column")));
8487  }
8488 }

References ereport, errcode(), errmsg(), ERROR, find_inheritance_children(), GETSTRUCT, HeapTupleIsValid, AlterTableCmd::name, RelationGetRelationName, RelationGetRelid, and SearchSysCacheCopyAttName().

Referenced by ATPrepCmd().

◆ ATPrepDropNotNull()

static void ATPrepDropNotNull ( Relation  rel,
bool  recurse,
bool  recursing 
)
static

Definition at line 7507 of file tablecmds.c.

7508 {
7509  /*
7510  * If the parent is a partitioned table, like check constraints, we do not
7511  * support removing the NOT NULL while partitions exist.
7512  */
7513  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7514  {
7515  PartitionDesc partdesc = RelationGetPartitionDesc(rel, true);
7516 
7517  Assert(partdesc != NULL);
7518  if (partdesc->nparts > 0 && !recurse && !recursing)
7519  ereport(ERROR,
7520  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
7521  errmsg("cannot remove constraint from only the partitioned table when partitions exist"),
7522  errhint("Do not specify the ONLY keyword.")));
7523  }
7524 }

References Assert, ereport, errcode(), errhint(), errmsg(), ERROR, PartitionDescData::nparts, RelationData::rd_rel, and RelationGetPartitionDesc().

Referenced by ATPrepCmd().

◆ ATPrepSetAccessMethod()

static void ATPrepSetAccessMethod ( AlteredTableInfo tab,
Relation  rel,
const char *  amname 
)
static

Definition at line 14733 of file tablecmds.c.

14734 {
14735  Oid amoid;
14736 
14737  /*
14738  * Look up the access method name and check that it differs from the
14739  * table's current AM. If DEFAULT was specified for a partitioned table
14740  * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
14741  */
14742  if (amname != NULL)
14743  amoid = get_table_am_oid(amname, false);
14744  else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14745  amoid = InvalidOid;
14746  else
14748 
14749  /* if it's a match, phase 3 doesn't need to do anything */
14750  if (rel->rd_rel->relam == amoid)
14751  return;
14752 
14753  /* Save info for Phase 3 to do the real work */
14755  tab->newAccessMethod = amoid;
14756  tab->chgAccessMethod = true;
14757 }
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().

◆ ATPrepSetNotNull()

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

Definition at line 7666 of file tablecmds.c.

7669 {
7670  /*
7671  * If we're already recursing, there's nothing to do; the topmost
7672  * invocation of ATSimpleRecursion already visited all children.
7673  */
7674  if (recursing)
7675  return;
7676 
7677  /*
7678  * If the target column is already marked NOT NULL, we can skip recursing
7679  * to children, because their columns should already be marked NOT NULL as
7680  * well. But there's no point in checking here unless the relation has
7681  * some children; else we can just wait till execution to check. (If it
7682  * does have children, however, this can save taking per-child locks
7683  * unnecessarily. This greatly improves concurrency in some parallel
7684  * restore scenarios.)
7685  *
7686  * Unfortunately, we can only apply this optimization to partitioned
7687  * tables, because traditional inheritance doesn't enforce that child
7688  * columns be NOT NULL when their parent is. (That's a bug that should
7689  * get fixed someday.)
7690  */
7691  if (rel->rd_rel->relhassubclass &&
7692  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7693  {
7694  HeapTuple tuple;
7695  bool attnotnull;
7696 
7697  tuple = SearchSysCacheAttName(RelationGetRelid(rel), cmd->name);
7698 
7699  /* Might as well throw the error now, if name is bad */
7700  if (!HeapTupleIsValid(tuple))
7701  ereport(ERROR,
7702  (errcode(ERRCODE_UNDEFINED_COLUMN),
7703  errmsg("column \"%s\" of relation \"%s\" does not exist",
7704  cmd->name, RelationGetRelationName(rel))));
7705 
7707  ReleaseSysCache(tuple);
7708  if (attnotnull)
7709  return;
7710  }
7711 
7712  /*
7713  * If we have ALTER TABLE ONLY ... SET NOT NULL on a partitioned table,
7714  * apply ALTER TABLE ... CHECK NOT NULL to every child. Otherwise, use
7715  * normal recursion logic.
7716  */
7717  if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
7718  !recurse)
7719  {
7721 
7722  newcmd->subtype = AT_CheckNotNull;
7723  newcmd->name = pstrdup(cmd->name);
7724  ATSimpleRecursion(wqueue, rel, newcmd, true, lockmode, context);
7725  }
7726  else
7727  ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
7728 }

References AT_CheckNotNull, ATSimpleRecursion(), attnotnull, context, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, HeapTupleIsValid, makeNode, AlterTableCmd::name, pstrdup(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), SearchSysCacheAttName(), and AlterTableCmd::subtype.

Referenced by ATParseTransformCmd(), and ATPrepCmd().

◆ ATPrepSetTableSpace()

static void ATPrepSetTableSpace ( AlteredTableInfo tab,
Relation  rel,
const char *  tablespacename,
LOCKMODE  lockmode 
)
static

Definition at line 14857 of file tablecmds.c.

14858 {
14859  Oid tablespaceId;
14860 
14861  /* Check that the tablespace exists */
14862  tablespaceId = get_tablespace_oid(tablespacename, false);
14863 
14864  /* Check permissions except when moving to database's default */
14865  if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace)
14866  {
14867  AclResult aclresult;
14868 
14869  aclresult = object_aclcheck(TableSpaceRelationId, tablespaceId, GetUserId(), ACL_CREATE);
14870  if (aclresult != ACLCHECK_OK)
14871  aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
14872  }
14873 
14874  /* Save info for Phase 3 to do the real work */
14875  if (OidIsValid(tab->newTableSpace))
14876  ereport(ERROR,
14877  (errcode(ERRCODE_SYNTAX_ERROR),
14878  errmsg("cannot have multiple SET TABLESPACE subcommands")));
14879 
14880  tab->newTableSpace = tablespaceId;
14881 }

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

5141 {
5142  ListCell *ltab;
5143 
5144  /*
5145  * We process all the tables "in parallel", one pass at a time. This is
5146  * needed because we may have to propagate work from one table to another
5147  * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5148  * re-adding of the foreign key constraint to the other table). Work can
5149  * only be propagated into later passes, however.
5150  */
5151  for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5152  {
5153  /* Go through each table that needs to be processed */
5154  foreach(ltab, *wqueue)
5155  {
5156  AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5157  List *subcmds = tab->subcmds[pass];
5158  ListCell *lcmd;
5159 
5160  if (subcmds == NIL)
5161  continue;
5162 
5163  /*
5164  * Open the relation and store it in tab. This allows subroutines
5165  * close and reopen, if necessary. Appropriate lock was obtained
5166  * by phase 1, needn't get it again.
5167  */
5168  tab->rel = relation_open(tab->relid, NoLock);
5169 
5170  foreach(lcmd, subcmds)
5171  ATExecCmd(wqueue, tab,
5172  lfirst_node(AlterTableCmd, lcmd),
5173  lockmode, pass, context);
5174 
5175  /*
5176  * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5177  * (this is not done in ATExecAlterColumnType since it should be
5178  * done only once if multiple columns of a table are altered).
5179  */
5180  if (pass == AT_PASS_ALTER_TYPE || pass == AT_PASS_SET_EXPRESSION)
5181  ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5182 
5183  if (tab->rel)
5184  {
5185  relation_close(tab->rel, NoLock);
5186  tab->rel = NULL;
5187  }
5188  }
5189  }
5190 
5191  /* Check to see if a toast table must be added. */
5192  foreach(ltab, *wqueue)
5193  {
5194  AlteredTableInfo *tab = (AlteredTableInfo *) lfirst(ltab);
5195 
5196  /*
5197  * If the table is source table of ATTACH PARTITION command, we did
5198  * not modify anything about it that will change its toasting
5199  * requirement, so no need to check.
5200  */
5201  if (((tab->relkind == RELKIND_RELATION ||
5202  tab->relkind == RELKIND_PARTITIONED_TABLE) &&
5203  tab->partition_constraint == NULL) ||
5204  tab->relkind == RELKIND_MATVIEW)
5205  AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5206  }
5207 }
Expr * partition_constraint
Definition: tablecmds.c:196
#define AT_NUM_PASSES
Definition: tablecmds.c:164
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
Definition: tablecmds.c:13701
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition: tablecmds.c:5213
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 5985 of file tablecmds.c.

5986 {
5987  Relation oldrel;
5988  Relation newrel;
5989  TupleDesc oldTupDesc;
5990  TupleDesc newTupDesc;
5991  bool needscan = false;
5992  List *notnull_attrs;
5993  int i;
5994  ListCell *l;
5995  EState *estate;
5996  CommandId mycid;
5997  BulkInsertState bistate;
5998  int ti_options;
5999  ExprState *partqualstate = NULL;
6000 
6001  /*
6002  * Open the relation(s). We have surely already locked the existing
6003  * table.
6004  */
6005  oldrel = table_open(tab->relid, NoLock);
6006  oldTupDesc = tab->oldDesc;
6007  newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6008 
6009  if (OidIsValid(OIDNewHeap))
6010  newrel = table_open(OIDNewHeap, lockmode);
6011  else
6012  newrel = NULL;
6013 
6014  /*
6015  * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6016  * is empty, so don't bother using it.
6017  */
6018  if (newrel)
6019  {
6020  mycid = GetCurrentCommandId(true);
6021  bistate = GetBulkInsertState();
6022  ti_options = TABLE_INSERT_SKIP_FSM;
6023  }
6024  else
6025  {
6026  /* keep compiler quiet about using these uninitialized */
6027  mycid = 0;
6028  bistate = NULL;
6029  ti_options = 0;
6030  }
6031 
6032  /*
6033  * Generate the constraint and default execution states
6034  */
6035 
6036  estate = CreateExecutorState();
6037 
6038  /* Build the needed expression execution states */
6039  foreach(l, tab->constraints)
6040  {
6041  NewConstraint *con = lfirst(l);
6042 
6043  switch (con->contype)
6044  {
6045  case CONSTR_CHECK:
6046  needscan = true;
6047  con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
6048  break;
6049  case CONSTR_FOREIGN:
6050  /* Nothing to do here */
6051  break;
6052  default:
6053  elog(ERROR, "unrecognized constraint type: %d",
6054  (int) con->contype);
6055  }
6056  }
6057 
6058  /* Build expression execution states for partition check quals */
6059  if (tab->partition_constraint)
6060  {
6061  needscan = true;
6062  partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6063  }
6064 
6065  foreach(l, tab->newvals)
6066  {
6067  NewColumnValue *ex = lfirst(l);
6068 
6069  /* expr already planned */
6070  ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
6071  }
6072 
6073  notnull_attrs = NIL;
6074  if (newrel || tab->verify_new_notnull)
6075  {
6076  /*
6077  * If we are rebuilding the tuples OR if we added any new but not
6078  * verified not-null constraints, check all not-null constraints. This
6079  * is a bit of overkill but it minimizes risk of bugs, and
6080  * heap_attisnull is a pretty cheap test anyway.
6081  */
6082  for (i = 0; i < newTupDesc->natts; i++)
6083  {
6084  Form_pg_attribute attr = TupleDescAttr(newTupDesc, i);
6085 
6086  if (attr->attnotnull && !attr->attisdropped)
6087  notnull_attrs = lappend_int(notnull_attrs, i);
6088  }
6089  if (notnull_attrs)
6090  needscan = true;
6091  }
6092 
6093  if (newrel || needscan)
6094  {
6095  ExprContext *econtext;
6096  TupleTableSlot *oldslot;
6097  TupleTableSlot *newslot;
6098  TableScanDesc scan;
6099  MemoryContext oldCxt;
6100  List *dropped_attrs = NIL;
6101  ListCell *lc;
6102  Snapshot snapshot;
6103 
6104  if (newrel)
6105  ereport(DEBUG1,
6106  (errmsg_internal("rewriting table \"%s\"",
6107  RelationGetRelationName(oldrel))));
6108  else
6109  ereport(DEBUG1,
6110  (errmsg_internal("verifying table \"%s\"",
6111  RelationGetRelationName(oldrel))));
6112 
6113  if (newrel)
6114  {
6115  /*
6116  * All predicate locks on the tuples or pages are about to be made
6117  * invalid, because we move tuples around. Promote them to
6118  * relation locks.
6119  */
6121  }
6122 
6123  econtext = GetPerTupleExprContext(estate);
6124 
6125  /*
6126  * Create necessary tuple slots. When rewriting, two slots are needed,
6127  * otherwise one suffices. In the case where one slot suffices, we
6128  * need to use the new tuple descriptor, otherwise some constraints
6129  * can't be evaluated. Note that even when the tuple layout is the
6130  * same and no rewrite is required, the tupDescs might not be
6131  * (consider ADD COLUMN without a default).
6132  */
6133  if (tab->rewrite)
6134  {
6135  Assert(newrel != NULL);
6136  oldslot = MakeSingleTupleTableSlot(oldTupDesc,
6137  table_slot_callbacks(oldrel));
6138  newslot = MakeSingleTupleTableSlot(newTupDesc,
6139  table_slot_callbacks(newrel));
6140 
6141  /*
6142  * Set all columns in the new slot to NULL initially, to ensure
6143  * columns added as part of the rewrite are initialized to NULL.
6144  * That is necessary as tab->newvals will not contain an
6145  * expression for columns with a NULL default, e.g. when adding a
6146  * column without a default together with a column with a default
6147  * requiring an actual rewrite.
6148  */
6149  ExecStoreAllNullTuple(newslot);
6150  }
6151  else
6152  {
6153  oldslot = MakeSingleTupleTableSlot(newTupDesc,
6154  table_slot_callbacks(oldrel));
6155  newslot = NULL;
6156  }
6157 
6158  /*
6159  * Any attributes that are dropped according to the new tuple
6160  * descriptor can be set to NULL. We precompute the list of dropped
6161  * attributes to avoid needing to do so in the per-tuple loop.
6162  */
6163  for (i = 0; i < newTupDesc->natts; i++)
6164  {
6165  if (TupleDescAttr(newTupDesc, i)->attisdropped)
6166  dropped_attrs = lappend_int(dropped_attrs, i);
6167  }
6168 
6169  /*
6170  * Scan through the rows, generating a new row if needed and then
6171  * checking all the constraints.
6172  */
6173  snapshot = RegisterSnapshot(GetLatestSnapshot());
6174  scan = table_beginscan(oldrel, snapshot, 0, NULL);
6175 
6176  /*
6177  * Switch to per-tuple memory context and reset it for each tuple
6178  * produced, so we don't leak memory.
6179  */
6181 
6182  while (table_scan_getnextslot(scan, ForwardScanDirection, oldslot))
6183  {
6184  TupleTableSlot *insertslot;
6185 
6186  if (tab->rewrite > 0)
6187  {
6188  /* Extract data from old tuple */
6189  slot_getallattrs(oldslot);
6190  ExecClearTuple(newslot);
6191 
6192  /* copy attributes */
6193  memcpy(newslot->tts_values, oldslot->tts_values,
6194  sizeof(Datum) * oldslot->tts_nvalid);
6195  memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6196  sizeof(bool) * oldslot->tts_nvalid);
6197 
6198  /* Set dropped attributes to null in new tuple */
6199  foreach(lc, dropped_attrs)
6200  newslot->tts_isnull[lfirst_int(lc)] = true;
6201 
6202  /*
6203  * Constraints and GENERATED expressions might reference the
6204  * tableoid column, so fill tts_tableOid with the desired
6205  * value. (We must do this each time, because it gets
6206  * overwritten with newrel's OID during storing.)
6207  */
6208  newslot->tts_tableOid = RelationGetRelid(oldrel);
6209 
6210  /*
6211  * Process supplied expressions to replace selected columns.
6212  *
6213  * First, evaluate expressions whose inputs come from the old
6214  * tuple.
6215  */
6216  econtext->ecxt_scantuple = oldslot;
6217 
6218  foreach(l, tab->newvals)
6219  {
6220  NewColumnValue *ex = lfirst(l);
6221 
6222  if (ex->is_generated)
6223  continue;
6224 
6225  newslot->tts_values[ex->attnum - 1]
6226  = ExecEvalExpr(ex->exprstate,
6227  econtext,
6228  &newslot->tts_isnull[ex->attnum - 1]);
6229  }
6230 
6231  ExecStoreVirtualTuple(newslot);
6232 
6233  /*
6234  * Now, evaluate any expressions whose inputs come from the
6235  * new tuple. We assume these columns won't reference each
6236  * other, so that there's no ordering dependency.
6237  */
6238  econtext->ecxt_scantuple = newslot;
6239 
6240  foreach(l, tab->newvals)
6241  {
6242  NewColumnValue *ex = lfirst(l);
6243 
6244  if (!ex->is_generated)
6245  continue;
6246 
6247  newslot->tts_values[ex->attnum - 1]
6248  = ExecEvalExpr(ex->exprstate,
6249  econtext,
6250  &newslot->tts_isnull[ex->attnum - 1]);
6251  }
6252 
6253  insertslot = newslot;
6254  }
6255  else
6256  {
6257  /*
6258  * If there's no rewrite, old and new table are guaranteed to
6259  * have the same AM, so we can just use the old slot to verify
6260  * new constraints etc.
6261  */
6262  insertslot = oldslot;
6263  }
6264 
6265  /* Now check any constraints on the possibly-changed tuple */
6266  econtext->ecxt_scantuple = insertslot;
6267 
6268  foreach(l, notnull_attrs)
6269  {
6270  int attn = lfirst_int(l);
6271 
6272  if (slot_attisnull(insertslot, attn + 1))
6273  {
6274  Form_pg_attribute attr = TupleDescAttr(newTupDesc, attn);
6275 
6276  ereport(ERROR,
6277  (errcode(ERRCODE_NOT_NULL_VIOLATION),
6278  errmsg("column \"%s\" of relation \"%s\" contains null values",
6279  NameStr(attr->attname),
6280  RelationGetRelationName(oldrel)),
6281  errtablecol(oldrel, attn + 1)));
6282  }
6283  }
6284 
6285  foreach(l, tab->constraints)
6286  {
6287  NewConstraint *con = lfirst(l);
6288 
6289  switch (con->contype)
6290  {
6291  case CONSTR_CHECK:
6292  if (!ExecCheck(con->qualstate, econtext))
6293  ereport(ERROR,
6294  (errcode(ERRCODE_CHECK_VIOLATION),
6295  errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6296  con->name,
6297  RelationGetRelationName(oldrel)),
6298  errtableconstraint(oldrel, con->name)));
6299  break;
6300  case CONSTR_FOREIGN:
6301  /* Nothing to do here */
6302  break;
6303  default:
6304  elog(ERROR, "unrecognized constraint type: %d",
6305  (int) con->contype);
6306  }
6307  }
6308 
6309  if (partqualstate && !ExecCheck(partqualstate, econtext))
6310  {
6311  if (tab->validate_default)
6312  ereport(ERROR,
6313  (errcode(ERRCODE_CHECK_VIOLATION),
6314  errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6315  RelationGetRelationName(oldrel)),
6316  errtable(oldrel)));
6317  else
6318  ereport(ERROR,
6319  (errcode(ERRCODE_CHECK_VIOLATION),
6320  errmsg("partition constraint of relation \"%s\" is violated by some row",
6321  RelationGetRelationName(oldrel)),
6322  errtable(oldrel)));
6323  }
6324 
6325  /* Write the tuple out to the new relation */
6326  if (newrel)
6327  table_tuple_insert(newrel, insertslot, mycid,
6328  ti_options, bistate);
6329 
6330  ResetExprContext(econtext);
6331 
6333  }
6334 
6335  MemoryContextSwitchTo(oldCxt);
6336  table_endscan(scan);
6337  UnregisterSnapshot(snapshot);
6338 
6340  if (newslot)
6342  }
6343 
6344  FreeExecutorState(estate);
6345 
6346  table_close(oldrel, NoLock);
6347  if (newrel)
6348  {
6349  FreeBulkInsertState(bistate);
6350 
6351  table_finish_bulk_insert(newrel, ti_options);
6352 
6353  table_close(newrel, NoLock);
6354  }
6355 }
uint32 CommandId
Definition: c.h:666
int errmsg_internal(const char *fmt,...)
Definition: elog.c:1157
#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:1927
void FreeBulkInsertState(BulkInsertState bistate)
Definition: heapam.c:1944
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:5969
Snapshot GetLatestSnapshot(void)
Definition: snapmgr.c:291
void UnregisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:836
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition: snapmgr.c:794
bool validate_default
Definition: tablecmds.c:198
TupleTableSlot * ecxt_scantuple
Definition: execnodes.h:255
AttrNumber attnum
Definition: tablecmds.c:233
bool is_generated
Definition: tablecmds.c:236
ExprState * exprstate
Definition: tablecmds.c:235
ExprState * qualstate
Definition: tablecmds.c:220
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:908
#define TABLE_INSERT_SKIP_FSM
Definition: tableam.h:260
static void table_finish_bulk_insert(Relation rel, int options)
Definition: tableam.h:1595
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, int options, struct BulkInsertStateData *bistate)
Definition: tableam.h:1402
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition: tableam.h:1055
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:827

References Assert, NewColumnValue::attnum, CHECK_FOR_INTERRUPTS, CONSTR_CHECK, CONSTR_FOREIGN, 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 5699 of file tablecmds.c.

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

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

6545 {
6546  int actual_target;
6547 
6548  switch (rel->rd_rel->relkind)
6549  {
6550  case RELKIND_RELATION:
6551  case RELKIND_PARTITIONED_TABLE:
6552  actual_target = ATT_TABLE;
6553  break;
6554  case RELKIND_VIEW:
6555  actual_target = ATT_VIEW;
6556  break;
6557  case RELKIND_MATVIEW:
6558  actual_target = ATT_MATVIEW;
6559  break;
6560  case RELKIND_INDEX:
6561  actual_target = ATT_INDEX;
6562  break;
6563  case RELKIND_PARTITIONED_INDEX:
6564  actual_target = ATT_PARTITIONED_INDEX;
6565  break;
6566  case RELKIND_COMPOSITE_TYPE:
6567  actual_target = ATT_COMPOSITE_TYPE;
6568  break;
6569  case RELKIND_FOREIGN_TABLE:
6570  actual_target = ATT_FOREIGN_TABLE;
6571  break;
6572  case RELKIND_SEQUENCE:
6573  actual_target = ATT_SEQUENCE;
6574  break;
6575  default:
6576  actual_target = 0;
6577  break;
6578  }
6579 
6580  /* Wrong target type? */
6581  if ((actual_target & allowed_targets) == 0)
6582  {
6583  const char *action_str = alter_table_type_to_string(cmdtype);
6584 
6585  if (action_str)
6586  ereport(ERROR,
6587  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6588  /* translator: %s is a group of some SQL keywords */
6589  errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6590  action_str, RelationGetRelationName(rel)),
6591  errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6592  else
6593  /* internal error? */
6594  elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6596  }
6597 
6598  /* Permissions checks */
6599  if (!object_ownercheck(RelationRelationId, RelationGetRelid(rel), GetUserId()))
6602 
6604  ereport(ERROR,
6605  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
6606  errmsg("permission denied: \"%s\" is a system catalog",
6607  RelationGetRelationName(rel))));
6608 }
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition: tablecmds.c:6395

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 ATAddCheckConstraint(), ATExecAddColumn(), ATExecAddInherit(), ATExecAttachPartition(), ATExecDropColumn(), ATExecDropConstraint(), and ATPrepCmd().

◆ ATSimpleRecursion()

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

Definition at line 6619 of file tablecmds.c.

6622 {
6623  /*
6624  * Propagate to children, if desired and if there are (or might be) any
6625  * children.
6626  */
6627  if (recurse && rel->rd_rel->relhassubclass)
6628  {
6629  Oid relid = RelationGetRelid(rel);
6630  ListCell *child;
6631  List *children;
6632 
6633  children = find_all_inheritors(relid, lockmode, NULL);
6634 
6635  /*
6636  * find_all_inheritors does the recursive search of the inheritance
6637  * hierarchy, so all we have to do is process all of the relids in the
6638  * list that it returns.
6639  */
6640  foreach(child, children)
6641  {
6642  Oid childrelid = lfirst_oid(child);
6643  Relation childrel;
6644 
6645  if (childrelid == relid)
6646  continue;
6647  /* find_all_inheritors already got lock */
6648  childrel = relation_open(childrelid, NoLock);
6649  CheckAlterTableIsSafe(childrel);
6650  ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6651  relation_close(childrel, NoLock);
6652  }
6653  }
6654 }

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

Referenced by ATPrepCmd(), and ATPrepSetNotNull().

◆ AttachPartitionEnsureIndexes()

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

Definition at line 18649 of file tablecmds.c.

18650 {
18651  List *idxes;
18652  List *attachRelIdxs;
18653  Relation *attachrelIdxRels;
18654  IndexInfo **attachInfos;
18655  ListCell *cell;
18656  MemoryContext cxt;
18657  MemoryContext oldcxt;
18658 
18660  "AttachPartitionEnsureIndexes",
18662  oldcxt = MemoryContextSwitchTo(cxt);
18663 
18664  idxes = RelationGetIndexList(rel);
18665  attachRelIdxs = RelationGetIndexList(attachrel);
18666  attachrelIdxRels = palloc(sizeof(Relation) * list_length(attachRelIdxs));
18667  attachInfos = palloc(sizeof(IndexInfo *) * list_length(attachRelIdxs));
18668 
18669  /* Build arrays of all existing indexes and their IndexInfos */
18670  foreach_oid(cldIdxId, attachRelIdxs)
18671  {
18672  int i = foreach_current_index(cldIdxId);
18673 
18674  attachrelIdxRels[i] = index_open(cldIdxId, AccessShareLock);
18675  attachInfos[i] = BuildIndexInfo(attachrelIdxRels[i]);
18676  }
18677 
18678  /*
18679  * If we're attaching a foreign table, we must fail if any of the indexes
18680  * is a constraint index; otherwise, there's nothing to do here. Do this
18681  * before starting work, to avoid wasting the effort of building a few
18682  * non-unique indexes before coming across a unique one.
18683  */
18684  if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
18685  {
18686  foreach(cell, idxes)
18687  {
18688  Oid idx = lfirst_oid(cell);
18690 
18691  if (idxRel->rd_index->indisunique ||
18692  idxRel->rd_index->indisprimary)
18693  ereport(ERROR,
18694  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
18695  errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
18696  RelationGetRelationName(attachrel),
18698  errdetail("Partitioned table \"%s\" contains unique indexes.",
18699  RelationGetRelationName(rel))));
18700  index_close(idxRel, AccessShareLock);
18701  }
18702 
18703  goto out;
18704  }
18705 
18706  /*
18707  * For each index on the partitioned table, find a matching one in the
18708  * partition-to-be; if one is not found, create one.
18709  */
18710  foreach(cell, idxes)
18711  {
18712  Oid idx = lfirst_oid(cell);
18714  IndexInfo *info;
18715  AttrMap *attmap;
18716  bool found = false;
18717  Oid constraintOid;
18718 
18719  /*
18720  * Ignore indexes in the partitioned table other than partitioned
18721  * indexes.
18722  */
18723  if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
18724  {
18725  index_close(idxRel, AccessShareLock);
18726  continue;
18727  }
18728 
18729  /* construct an indexinfo to compare existing indexes against */
18730  info = BuildIndexInfo(idxRel);
18731  attmap = build_attrmap_by_name(RelationGetDescr(attachrel),
18732  RelationGetDescr(rel),
18733  false);
18734  constraintOid = get_relation_idx_constraint_oid(RelationGetRelid(rel), idx);
18735 
18736  /*
18737  * Scan the list of existing indexes in the partition-to-be, and mark
18738  * the first matching, valid, unattached one we find, if any, as
18739  * partition of the parent index. If we find one, we're done.
18740  */
18741  for (int i = 0; i < list_length(attachRelIdxs); i++)
18742  {
18743  Oid cldIdxId = RelationGetRelid(attachrelIdxRels[i]);
18744  Oid cldConstrOid = InvalidOid;
18745 
18746  /* does this index have a parent? if so, can't use it */
18747  if (attachrelIdxRels[i]->rd_rel->relispartition)
18748  continue;
18749 
18750  /* If this index is invalid, can't use it */
18751  if (!attachrelIdxRels[i]->rd_index->indisvalid)
18752  continue;
18753 
18754  if (CompareIndexInfo(attachInfos[i], info,
18755  attachrelIdxRels[i]->rd_indcollation,
18756  idxRel->rd_indcollation,
18757  attachrelIdxRels[i]->rd_opfamily,
18758  idxRel->rd_opfamily,
18759  attmap))
18760  {
18761  /*
18762  * If this index is being created in the parent because of a
18763  * constraint, then the child needs to have a constraint also,
18764  * so look for one. If there is no such constraint, this
18765  * index is no good, so keep looking.
18766  */
18767  if (OidIsValid(constraintOid))
18768  {
18769  cldConstrOid =
18771  cldIdxId);
18772  /* no dice */
18773  if (!OidIsValid(cldConstrOid))
18774  continue;
18775 
18776  /* Ensure they're both the same type of constraint */
18777  if (get_constraint_type(constraintOid) !=
18778  get_constraint_type(cldConstrOid))
18779  continue;
18780  }
18781 
18782  /* bingo. */
18783  IndexSetParentIndex(attachrelIdxRels[i], idx);
18784  if (OidIsValid(constraintOid))
18785  ConstraintSetParentConstraint(cldConstrOid, constraintOid,
18786  RelationGetRelid(attachrel));
18787  found = true;
18788 
18790  break;
18791  }
18792  }
18793 
18794  /*
18795  * If no suitable index was found in the partition-to-be, create one
18796  * now.
18797  */
18798  if (!found)
18799  {
18800  IndexStmt *stmt;
18801  Oid conOid;
18802 
18804  idxRel, attmap,
18805  &conOid);
18807  RelationGetRelid(idxRel),
18808  conOid,
18809  -1,
18810  true, false, false, false, false);
18811  }
18812 
18813  index_close(idxRel, AccessShareLock);
18814  }
18815 
18816 out:
18817  /* Clean up. */
18818  for (int i = 0; i < list_length(attachRelIdxs); i++)
18819  index_close(attachrelIdxRels[i], AccessShareLock);
18820  MemoryContextSwitchTo(oldcxt);
18821  MemoryContextDelete(cxt);
18822 }
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

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

Referenced by attachPartitionTable().

◆ attachPartitionTable()

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

Definition at line 18321 of file tablecmds.c.

18322 {
18323  /* OK to create inheritance. Rest of the checks performed there */
18324  CreateInheritance(attachrel, rel, true);
18325 
18326  /* Update the pg_class entry. */
18327  StorePartitionBound(attachrel, rel, bound);
18328 
18329  /* Ensure there exists a correct set of indexes in the partition. */
18330  AttachPartitionEnsureIndexes(wqueue, rel, attachrel);
18331 
18332  /* and triggers */
18333  CloneRowTriggersToPartition(rel, attachrel);
18334 
18335  /*
18336  * Clone foreign key constraints. Callee is responsible for setting up
18337  * for phase 3 constraint verification.
18338  */
18339  CloneForeignKeyConstraints(wqueue, rel, attachrel);
18340 }
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition: heap.c:3481
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
Definition: tablecmds.c:18830
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
Definition: tablecmds.c:18649
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:10513

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

6696 {
6697  ListCell *child;
6698  List *children;
6699 
6700  Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6701 
6702  children = find_typed_table_dependencies(rel->rd_rel->reltype,
6704  cmd->behavior);
6705 
6706  foreach(child, children)
6707  {
6708  Oid childrelid = lfirst_oid(child);
6709  Relation childrel;
6710 
6711  childrel = relation_open(childrelid, lockmode);
6712  CheckAlterTableIsSafe(childrel);
6713  ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6714  relation_close(childrel, NoLock);
6715  }
6716 }
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition: tablecmds.c:6897

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

1282 {
1283  int natts;
1285  ListCell *l;
1286  TupleDesc desc;
1287  char *attname;
1288  Oid atttypid;
1289  int32 atttypmod;
1290  Oid attcollation;
1291  int attdim;
1292 
1293  /*
1294  * allocate a new tuple descriptor
1295  */
1296  natts = list_length(columns);
1297  desc = CreateTemplateTupleDesc(natts);
1298 
1299  attnum = 0;
1300 
1301  foreach(l, columns)
1302  {
1303  ColumnDef *entry = lfirst(l);
1304  AclResult aclresult;
1305  Form_pg_attribute att;
1306 
1307  /*
1308  * for each entry in the list, get the name and type information from
1309  * the list and have TupleDescInitEntry fill in the attribute
1310  * information we need.
1311  */
1312  attnum++;
1313 
1314  attname = entry->colname;
1315  typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1316 
1317  aclresult = object_aclcheck(TypeRelationId, atttypid, GetUserId(), ACL_USAGE);
1318  if (aclresult != ACLCHECK_OK)
1319  aclcheck_error_type(aclresult, atttypid);
1320 
1321  attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1322  attdim = list_length(entry->typeName->arrayBounds);
1323  if (attdim > PG_INT16_MAX)
1324  ereport(ERROR,
1325  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
1326  errmsg("too many array dimensions"));
1327 
1328  if (entry->typeName->setof)
1329  ereport(ERROR,
1330  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1331  errmsg("column \"%s\" cannot be declared SETOF",
1332  attname)));
1333 
1335  atttypid, atttypmod, attdim);
1336  att = TupleDescAttr(desc, attnum - 1);
1337 
1338  /* Override TupleDescInitEntry's settings as requested */
1339  TupleDescInitEntryCollation(desc, attnum, attcollation);
1340 
1341  /* Fill in additional stuff not handled by TupleDescInitEntry */
1342  att->attnotnull = entry->is_not_null;
1343  att->attislocal = entry->is_local;
1344  att->attinhcount = entry->inhcount;
1345  att->attidentity = entry->identity;
1346  att->attgenerated = entry->generated;
1347  att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1348  if (entry->storage)
1349  att->attstorage = entry->storage;
1350  else if (entry->storage_name)
1351  att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1352  }
1353 
1354  return desc;
1355 }
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 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, ColumnDef::colname, ColumnDef::compression, CreateTemplateTupleDesc(), ereport, errcode(), errmsg(), ERROR, ColumnDef::generated, GetAttributeCompression(), GetAttributeStorage(), GetColumnDefCollation(), GetUserId(), ColumnDef::identity, ColumnDef::inhcount, ColumnDef::is_local, ColumnDef::is_not_null, lfirst, list_length(), object_aclcheck(), PG_INT16_MAX, 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 14555 of file tablecmds.c.

14556 {
14557  Relation attRelation;
14558  SysScanDesc scan;
14559  ScanKeyData key[1];
14560  HeapTuple attributeTuple;
14561 
14562  attRelation = table_open(AttributeRelationId, RowExclusiveLock);
14563  ScanKeyInit(&key[0],
14564  Anum_pg_attribute_attrelid,
14565  BTEqualStrategyNumber, F_OIDEQ,
14566  ObjectIdGetDatum(relationOid));
14567  scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
14568  true, NULL, 1, key);
14569  while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
14570  {
14571  Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
14572  Datum repl_val[Natts_pg_attribute];
14573  bool repl_null[Natts_pg_attribute];
14574  bool repl_repl[Natts_pg_attribute];
14575  Acl *newAcl;
14576  Datum aclDatum;
14577  bool isNull;
14578  HeapTuple newtuple;
14579 
14580  /* Ignore dropped columns */
14581  if (att->attisdropped)
14582  continue;
14583 
14584  aclDatum = heap_getattr(attributeTuple,
14585  Anum_pg_attribute_attacl,
14586  RelationGetDescr(attRelation),
14587  &isNull);
14588  /* Null ACLs do not require changes */
14589  if (isNull)
14590  continue;
14591 
14592  memset(repl_null, false, sizeof(repl_null));
14593  memset(repl_repl, false, sizeof(repl_repl));
14594 
14595  newAcl = aclnewowner(DatumGetAclP(aclDatum),
14596  oldOwnerId, newOwnerId);
14597  repl_repl[Anum_pg_attribute_attacl - 1] = true;
14598  repl_val[Anum_pg_attribute_attacl - 1] = PointerGetDatum(newAcl);
14599 
14600  newtuple = heap_modify_tuple(attributeTuple,
14601  RelationGetDescr(attRelation),
14602  repl_val, repl_null, repl_repl);
14603 
14604  CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
14605 
14606  heap_freetuple(newtuple);
14607  }
14608  systable_endscan(scan);
14609  table_close(attRelation, RowExclusiveLock);
14610 }

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

14621 {
14622  Relation depRel;
14623  SysScanDesc scan;
14624  ScanKeyData key[2];
14625  HeapTuple tup;
14626 
14627  /*
14628  * SERIAL sequences are those having an auto dependency on one of the
14629  * table's columns (we don't care *which* column, exactly).
14630  */
14631  depRel = table_open(DependRelationId, AccessShareLock);
14632 
14633  ScanKeyInit(&key[0],
14634  Anum_pg_depend_refclassid,
14635  BTEqualStrategyNumber, F_OIDEQ,
14636  ObjectIdGetDatum(RelationRelationId));
14637  ScanKeyInit(&key[1],
14638  Anum_pg_depend_refobjid,
14639  BTEqualStrategyNumber, F_OIDEQ,
14640  ObjectIdGetDatum(relationOid));
14641  /* we leave refobjsubid unspecified */
14642 
14643  scan = systable_beginscan(depRel, DependReferenceIndexId, true,
14644  NULL, 2, key);
14645 
14646  while (HeapTupleIsValid(tup = systable_getnext(scan)))
14647  {
14648  Form_pg_depend depForm = (Form_pg_depend) GETSTRUCT(tup);
14649  Relation seqRel;
14650 
14651  /* skip dependencies other than auto dependencies on columns */
14652  if (depForm->refobjsubid == 0 ||
14653  depForm->classid != RelationRelationId ||
14654  depForm->objsubid != 0 ||
14655  !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
14656  continue;
14657 
14658  /* Use relation_open just in case it's an index */
14659  seqRel = relation_open(depForm->objid, lockmode);
14660 
14661  /* skip non-sequence relations */
14662  if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
14663  {
14664  /* No need to keep the lock */
14665  relation_close(seqRel, lockmode);
14666  continue;
14667  }
14668 
14669  /* We don't need to close the sequence while we alter it. */
14670  ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
14671 
14672  /* Now we can close it. Keep the lock till end of transaction. */
14673  relation_close(seqRel, NoLock);
14674  }
14675 
14676  systable_endscan(scan);
14677 
14679 }

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

7415 {
7416  HeapTuple attTuple;
7417  int attnum;
7418 
7419  /*
7420  * this test is deliberately not attisdropped-aware, since if one tries to
7421  * add a column matching a dropped column name, it's gonna fail anyway.
7422  */
7423  attTuple = SearchSysCache2(ATTNAME,
7425  PointerGetDatum(colname));
7426  if (!HeapTupleIsValid(attTuple))
7427  return true;
7428 
7429  attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
7430  ReleaseSysCache(attTuple);
7431 
7432  /*
7433  * We throw a different error message for conflicts with system column
7434  * names, since they are normally not shown and the user might otherwise
7435  * be confused about the reason for the conflict.
7436  */
7437  if (attnum <= 0)
7438  ereport(ERROR,
7439  (errcode(ERRCODE_DUPLICATE_COLUMN),
7440  errmsg("column name \"%s\" conflicts with a system column name",
7441  colname)));
7442  else
7443  {
7444  if (if_not_exists)
7445  {
7446  ereport(NOTICE,
7447  (errcode(ERRCODE_DUPLICATE_COLUMN),
7448  errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7449  colname, RelationGetRelationName(rel))));
7450  return false;
7451  }
7452 
7453  ereport(ERROR,
7454  (errcode(ERRCODE_DUPLICATE_COLUMN),
7455  errmsg("column \"%s\" of relation \"%s\" already exists",
7456  colname, RelationGetRelationName(rel))));
7457  }
7458 
7459  return true;
7460 }
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 6946 of file tablecmds.c.

6947 {
6948  Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple);
6949  bool typeOk = false;
6950 
6951  if (typ->typtype == TYPTYPE_COMPOSITE)
6952  {
6953  Relation typeRelation;
6954 
6955  Assert(OidIsValid(typ->typrelid));
6956  typeRelation = relation_open(typ->typrelid, AccessShareLock);
6957  typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6958 
6959  /*
6960  * Close the parent rel, but keep our AccessShareLock on it until xact
6961  * commit. That will prevent someone else from deleting or ALTERing
6962  * the type before the typed table creation/conversion commits.
6963  */
6964  relation_close(typeRelation, NoLock);
6965 
6966  if (!typeOk)
6967  ereport(ERROR,
6968  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6969  errmsg("type %s is the row type of another table",
6970  format_type_be(typ->oid)),
6971  errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
6972  }
6973  else
6974  ereport(ERROR,
6975  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
6976  errmsg("type %s is not a composite type",
6977  format_type_be(typ->oid))));
6978 }

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

Referenced by ATExecAddOf(), and transformOfType().

◆ CheckAlterTableIsSafe()

static void CheckAlterTableIsSafe ( Relation  rel)
static

Definition at line 4277 of file tablecmds.c.

4278 {
4279  /*
4280  * Don't allow ALTER on temp tables of other backends. Their local buffer
4281  * manager is not going to cope if we need to change the table's contents.
4282  * Even if we don't, there may be optimizations that assume temp tables
4283  * aren't subject to such interference.
4284  */
4285  if (RELATION_IS_OTHER_TEMP(rel))
4286  ereport(ERROR,
4287  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
4288  errmsg("cannot alter temporary tables of other sessions")));
4289 
4290  /*
4291  * Also check for active uses of the relation in the current transaction,
4292  * including open scans and pending AFTER trigger events.
4293  */
4294  CheckTableNotInUse(rel, "ALTER TABLE");
4295 }
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition: tablecmds.c:4244

References CheckTableNotInUse(), ereport, errcode(), errmsg(), ERROR, and RELATION_IS_OTHER_TEMP.

Referenced by addFkRecurseReferencing(), AlterTable(), ATAddCheckConstraint(), ATCheckPartitionsNotInUse(), ATExecAddColumn(), ATExecDropColumn(), ATExecDropConstraint(), ATPrepAlterColumnType(), ATSimpleRecursion(), and ATTypedTableRecursion().

◆ checkFkeyPermissions()

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

Definition at line 12084 of file tablecmds.c.

12085 {
12086  Oid roleid = GetUserId();
12087  AclResult aclresult;
12088  int i;
12089 
12090  /* Okay if we have relation-level REFERENCES permission */
12091  aclresult = pg_class_aclcheck(RelationGetRelid(rel), roleid,
12092  ACL_REFERENCES);
12093  if (aclresult == ACLCHECK_OK)
12094  return;
12095  /* Else we must have REFERENCES on each column */
12096  for (i = 0; i < natts; i++)
12097  {
12098  aclresult = pg_attribute_aclcheck(RelationGetRelid(rel), attnums[i],
12099  roleid, ACL_REFERENCES);
12100  if (aclresult != ACLCHECK_OK)
12101  aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind),
12103  }
12104 }
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition: aclchk.c:3920
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition: aclchk.c:4091
#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 3530 of file tablecmds.c.

3531 {
3532  Oid oldTableSpaceId;
3533 
3534  /*
3535  * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3536  * stored as 0.
3537  */
3538  oldTableSpaceId = rel->rd_rel->reltablespace;
3539  if (newTableSpaceId == oldTableSpaceId ||
3540  (newTableSpaceId == MyDatabaseTableSpace && oldTableSpaceId == 0))
3541  return false;
3542 
3543  /*
3544  * We cannot support moving mapped relations into different tablespaces.
3545  * (In particular this eliminates all shared catalogs.)
3546  */
3547  if (RelationIsMapped(rel))
3548  ereport(ERROR,
3549  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3550  errmsg("cannot move system relation \"%s\"",
3551  RelationGetRelationName(rel))));
3552 
3553  /* Cannot move a non-shared relation into pg_global */
3554  if (newTableSpaceId == GLOBALTABLESPACE_OID)
3555  ereport(ERROR,
3556  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
3557  errmsg("only shared relations can be placed in pg_global tablespace")));
3558 
3559  /*
3560  * Do not allow moving temp tables of other backends ... their local
3561  * buffer manager is not going to cope.
3562  */
3563  if (RELATION_IS_OTHER_TEMP(rel))
3564  ereport(ERROR,
3565  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3566  errmsg("cannot move temporary tables of other sessions")));
3567 
3568  return true;
3569 }
#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 4244 of file tablecmds.c.

4245 {
4246  int expected_refcnt;
4247 
4248  expected_refcnt = rel->rd_isnailed ? 2 : 1;
4249  if (rel->rd_refcnt != expected_refcnt)
4250  ereport(ERROR,
4251  (errcode(ERRCODE_OBJECT_IN_USE),
4252  /* translator: first %s is a SQL command, eg ALTER TABLE */
4253  errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4254  stmt, RelationGetRelationName(rel))));
4255 
4256  if (rel->rd_rel->relkind != RELKIND_INDEX &&
4257  rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4259  ereport(ERROR,
4260  (errcode(ERRCODE_OBJECT_IN_USE),
4261  /* translator: first %s is a SQL command, eg ALTER TABLE */
4262  errmsg("cannot %s \"%s\" because it has pending trigger events",
4263  stmt, RelationGetRelationName(rel))));
4264 }
int rd_refcnt
Definition: rel.h:59
bool rd_isnailed
Definition: rel.h:62
bool AfterTriggerPendingOnRel(Oid relid)
Definition: trigger.c:5976

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

Referenced by CheckAlterTableIsSafe(), cluster_rel(), DefineIndex(), DefineVirtualRelation(), heap_drop_with_catalog(), index_drop(), MergeAttributes(), RefreshMatViewByOid(), reindex_index(), and truncate_check_activity().

◆ ChooseForeignKeyConstraintNameAddition()

static char * ChooseForeignKeyConstraintNameAddition ( List colnames)
static

Definition at line 9403 of file tablecmds.c.

9404 {
9405  char buf[NAMEDATALEN * 2];
9406  int buflen = 0;
9407  ListCell *lc;
9408 
9409  buf[0] = '\0';
9410  foreach(lc, colnames)
9411  {
9412  const char *name = strVal(lfirst(lc));
9413 
9414  if (buflen > 0)
9415  buf[buflen++] = '_'; /* insert _ between names */
9416 
9417  /*
9418  * At this point we have buflen <= NAMEDATALEN. name should be less
9419  * than NAMEDATALEN already, but use strlcpy for paranoia.
9420  */
9421  strlcpy(buf + buflen, name, NAMEDATALEN);
9422  buflen += strlen(buf + buflen);
9423  if (buflen >= NAMEDATALEN)
9424  break;
9425  }
9426  return pstrdup(buf);
9427 }
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 10544 of file tablecmds.c.

10545 {
10546  Relation pg_constraint;
10547  AttrMap *attmap;
10548  ListCell *cell;
10549  SysScanDesc scan;
10550  ScanKeyData key[2];
10551  HeapTuple tuple;
10552  List *clone = NIL;
10553  Relation trigrel;
10554 
10555  /*
10556  * Search for any constraints where this partition's parent is in the
10557  * referenced side. However, we must not clone any constraint whose
10558  * parent constraint is also going to be cloned, to avoid duplicates. So
10559  * do it in two steps: first construct the list of constraints to clone,
10560  * then go over that list cloning those whose parents are not in the list.
10561  * (We must not rely on the parent being seen first, since the catalog
10562  * scan could return children first.)
10563  */
10564  pg_constraint = table_open(ConstraintRelationId, RowShareLock);
10565  ScanKeyInit(&key[0],
10566  Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
10567  F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel)));
10568  ScanKeyInit(&key[1],
10569  Anum_pg_constraint_contype, BTEqualStrategyNumber,
10570  F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
10571  /* This is a seqscan, as we don't have a usable index ... */
10572  scan = systable_beginscan(pg_constraint, InvalidOid, true,
10573  NULL, 2, key);
10574  while ((tuple = systable_getnext(scan)) != NULL)
10575  {
10576  Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10577 
10578  clone = lappend_oid(clone, constrForm->oid);
10579  }
10580  systable_endscan(scan);
10581  table_close(pg_constraint, RowShareLock);
10582 
10583  /*
10584  * Triggers of the foreign keys will be manipulated a bunch of times in
10585  * the loop below. To avoid repeatedly opening/closing the trigger
10586  * catalog relation, we open it here and pass it to the subroutines called
10587  * below.
10588  */
10589  trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10590 
10591  attmap = build_attrmap_by_name(RelationGetDescr(partitionRel),
10592  RelationGetDescr(parentRel),
10593  false);
10594  foreach(cell, clone)
10595  {
10596  Oid constrOid = lfirst_oid(cell);
10597  Form_pg_constraint constrForm;
10598  Relation fkRel;
10599  Oid indexOid;
10600  Oid partIndexId;
10601  int numfks;
10602  AttrNumber conkey[INDEX_MAX_KEYS];
10603  AttrNumber mapped_confkey[INDEX_MAX_KEYS];
10604  AttrNumber confkey[INDEX_MAX_KEYS];
10605  Oid conpfeqop[INDEX_MAX_KEYS];
10606  Oid conppeqop[INDEX_MAX_KEYS];
10607  Oid conffeqop[INDEX_MAX_KEYS];
10608  int numfkdelsetcols;
10609  AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10610  Constraint *fkconstraint;
10611  Oid deleteTriggerOid,
10612  updateTriggerOid;
10613 
10614  tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constrOid));
10615  if (!HeapTupleIsValid(tuple))
10616  elog(ERROR, "cache lookup failed for constraint %u", constrOid);
10617  constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10618 
10619  /*
10620  * As explained above: don't try to clone a constraint for which we're
10621  * going to clone the parent.
10622  */
10623  if (list_member_oid(clone, constrForm->conparentid))
10624  {
10625  ReleaseSysCache(tuple);
10626  continue;
10627  }
10628 
10629  /*
10630  * Don't clone self-referencing foreign keys, which can be in the
10631  * partitioned table or in the partition-to-be.
10632  */
10633  if (constrForm->conrelid == RelationGetRelid(parentRel) ||
10634  constrForm->conrelid == RelationGetRelid(partitionRel))
10635  {
10636  ReleaseSysCache(tuple);
10637  continue;
10638  }
10639 
10640  /*
10641  * Because we're only expanding the key space at the referenced side,
10642  * we don't need to prevent any operation in the referencing table, so
10643  * AccessShareLock suffices (assumes that dropping the constraint
10644  * acquires AEL).
10645  */
10646  fkRel = table_open(constrForm->conrelid, AccessShareLock);
10647 
10648  indexOid = constrForm->conindid;
10650  &numfks,
10651  conkey,
10652  confkey,
10653  conpfeqop,
10654  conppeqop,
10655  conffeqop,
10656  &numfkdelsetcols,
10657  confdelsetcols);
10658 
10659  for (int i = 0; i < numfks; i++)
10660  mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
10661 
10662  fkconstraint = makeNode(Constraint);
10663  fkconstraint->contype = CONSTRAINT_FOREIGN;
10664  fkconstraint->conname = NameStr(constrForm->conname);
10665  fkconstraint->deferrable = constrForm->condeferrable;
10666  fkconstraint->initdeferred = constrForm->condeferred;
10667  fkconstraint->location = -1;
10668  fkconstraint->pktable = NULL;
10669  /* ->fk_attrs determined below */
10670  fkconstraint->pk_attrs = NIL;
10671  fkconstraint->fk_matchtype = constrForm->confmatchtype;
10672  fkconstraint->fk_upd_action = constrForm->confupdtype;
10673  fkconstraint->fk_del_action = constrForm->confdeltype;
10674  fkconstraint->fk_del_set_cols = NIL;
10675  fkconstraint->old_conpfeqop = NIL;
10676  fkconstraint->old_pktable_oid = InvalidOid;
10677  fkconstraint->skip_validation = false;
10678  fkconstraint->initially_valid = true;
10679 
10680  /* set up colnames that are used to generate the constraint name */
10681  for (int i = 0; i < numfks; i++)
10682  {
10683  Form_pg_attribute att;
10684 
10685  att = TupleDescAttr(RelationGetDescr(fkRel),
10686  conkey[i] - 1);
10687  fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10688  makeString(NameStr(att->attname)));
10689  }
10690 
10691  /*
10692  * Add the new foreign key constraint pointing to the new partition.
10693  * Because this new partition appears in the referenced side of the
10694  * constraint, we don't need to set up for Phase 3 check.
10695  */
10696  partIndexId = index_get_partition(partitionRel, indexOid);
10697  if (!OidIsValid(partIndexId))
10698  elog(ERROR, "index for %u not found in partition %s",
10699  indexOid, RelationGetRelationName(partitionRel));
10700 
10701  /*
10702  * Get the "action" triggers belonging to the constraint to pass as
10703  * parent OIDs for similar triggers that will be created on the
10704  * partition in addFkRecurseReferenced().
10705  */
10706  GetForeignKeyActionTriggers(trigrel, constrOid,
10707  constrForm->confrelid, constrForm->conrelid,
10708  &deleteTriggerOid, &updateTriggerOid);
10709 
10711  fkconstraint,
10712  fkRel,
10713  partitionRel,
10714  partIndexId,
10715  constrOid,
10716  numfks,
10717  mapped_confkey,
10718  conkey,
10719  conpfeqop,
10720  conppeqop,
10721  conffeqop,
10722  numfkdelsetcols,
10723  confdelsetcols,
10724  true,
10725  deleteTriggerOid,
10726  updateTriggerOid);
10727 
10728  table_close(fkRel, NoLock);
10729  ReleaseSysCache(tuple);
10730  }
10731 
10732  table_close(trigrel, RowExclusiveLock);
10733 }
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
ParseLoc location
Definition: parsenodes.h:2777
static void GetForeignKeyActionTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:11160
String * makeString(char *str)
Definition: value.c:63

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

10750 {
10751  AttrMap *attmap;
10752  List *partFKs;
10753  List *clone = NIL;
10754  ListCell *cell;
10755  Relation trigrel;
10756 
10757  /* obtain a list of constraints that we need to clone */
10758  foreach(cell, RelationGetFKeyList(parentRel))
10759  {
10760  ForeignKeyCacheInfo *fk = lfirst(cell);
10761 
10762  clone = lappend_oid(clone, fk->conoid);
10763  }
10764 
10765  /*
10766  * Silently do nothing if there's nothing to do. In particular, this
10767  * avoids throwing a spurious error for foreign tables.
10768  */
10769  if (clone == NIL)
10770  return;
10771 
10772  if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
10773  ereport(ERROR,
10774  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
10775  errmsg("foreign key constraints are not supported on foreign tables")));
10776 
10777  /*
10778  * Triggers of the foreign keys will be manipulated a bunch of times in
10779  * the loop below. To avoid repeatedly opening/closing the trigger
10780  * catalog relation, we open it here and pass it to the subroutines called
10781  * below.
10782  */
10783  trigrel = table_open(TriggerRelationId, RowExclusiveLock);
10784 
10785  /*
10786  * The constraint key may differ, if the columns in the partition are
10787  * different. This map is used to convert them.
10788  */
10789  attmap = build_attrmap_by_name(RelationGetDescr(partRel),
10790  RelationGetDescr(parentRel),
10791  false);
10792 
10793  partFKs = copyObject(RelationGetFKeyList(partRel));
10794 
10795  foreach(cell, clone)
10796  {
10797  Oid parentConstrOid = lfirst_oid(cell);
10798  Form_pg_constraint constrForm;
10799  Relation pkrel;
10800  HeapTuple tuple;
10801  int numfks;
10802  AttrNumber conkey[INDEX_MAX_KEYS];
10803  AttrNumber mapped_conkey[INDEX_MAX_KEYS];
10804  AttrNumber confkey[INDEX_MAX_KEYS];
10805  Oid conpfeqop[INDEX_MAX_KEYS];
10806  Oid conppeqop[INDEX_MAX_KEYS];
10807  Oid conffeqop[INDEX_MAX_KEYS];
10808  int numfkdelsetcols;
10809  AttrNumber confdelsetcols[INDEX_MAX_KEYS];
10810  Constraint *fkconstraint;
10811  bool attached;
10812  Oid indexOid;
10813  Oid constrOid;
10814  ObjectAddress address,
10815  referenced;
10816  ListCell *lc;
10817  Oid insertTriggerOid,
10818  updateTriggerOid;
10819 
10820  tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(parentConstrOid));
10821  if (!HeapTupleIsValid(tuple))
10822  elog(ERROR, "cache lookup failed for constraint %u",
10823  parentConstrOid);
10824  constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
10825 
10826  /* Don't clone constraints whose parents are being cloned */
10827  if (list_member_oid(clone, constrForm->conparentid))
10828  {
10829  ReleaseSysCache(tuple);
10830  continue;
10831  }
10832 
10833  /*
10834  * Need to prevent concurrent deletions. If pkrel is a partitioned
10835  * relation, that means to lock all partitions.
10836  */
10837  pkrel = table_open(constrForm->confrelid, ShareRowExclusiveLock);
10838  if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10839  (void) find_all_inheritors(RelationGetRelid(pkrel),
10840  ShareRowExclusiveLock, NULL);
10841 
10842  DeconstructFkConstraintRow(tuple, &numfks, conkey, confkey,
10843  conpfeqop, conppeqop, conffeqop,
10844  &numfkdelsetcols, confdelsetcols);
10845  for (int i = 0; i < numfks; i++)
10846  mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
10847 
10848  /*
10849  * Get the "check" triggers belonging to the constraint to pass as
10850  * parent OIDs for similar triggers that will be created on the
10851  * partition in addFkRecurseReferencing(). They are also passed to
10852  * tryAttachPartitionForeignKey() below to simply assign as parents to
10853  * the partition's existing "check" triggers, that is, if the
10854  * corresponding constraints is deemed attachable to the parent
10855  * constraint.
10856  */
10857  GetForeignKeyCheckTriggers(trigrel, constrForm->oid,
10858  constrForm->confrelid, constrForm->conrelid,
10859  &insertTriggerOid, &updateTriggerOid);
10860 
10861  /*
10862  * Before creating a new constraint, see whether any existing FKs are
10863  * fit for the purpose. If one is, attach the parent constraint to
10864  * it, and don't clone anything. This way we avoid the expensive
10865  * verification step and don't end up with a duplicate FK, and we
10866  * don't need to recurse to partitions for this constraint.
10867  */
10868  attached = false;
10869  foreach(lc, partFKs)
10870  {
10872 
10874  RelationGetRelid(partRel),
10875  parentConstrOid,
10876  numfks,
10877  mapped_conkey,
10878  confkey,
10879  conpfeqop,
10880  insertTriggerOid,
10881  updateTriggerOid,
10882  trigrel))
10883  {
10884  attached = true;
10885  table_close(pkrel, NoLock);
10886  break;
10887  }
10888  }
10889  if (attached)
10890  {
10891  ReleaseSysCache(tuple);
10892  continue;
10893  }
10894 
10895  /* No dice. Set up to create our own constraint */
10896  fkconstraint = makeNode(Constraint);
10897  fkconstraint->contype = CONSTRAINT_FOREIGN;
10898  /* ->conname determined below */
10899  fkconstraint->deferrable = constrForm->condeferrable;
10900  fkconstraint->initdeferred = constrForm->condeferred;
10901  fkconstraint->location = -1;
10902  fkconstraint->pktable = NULL;
10903  /* ->fk_attrs determined below */
10904  fkconstraint->pk_attrs = NIL;
10905  fkconstraint->fk_matchtype = constrForm->confmatchtype;
10906  fkconstraint->fk_upd_action = constrForm->confupdtype;
10907  fkconstraint->fk_del_action = constrForm->confdeltype;
10908  fkconstraint->fk_del_set_cols = NIL;
10909  fkconstraint->old_conpfeqop = NIL;
10910  fkconstraint->old_pktable_oid = InvalidOid;
10911  fkconstraint->skip_validation = false;
10912  fkconstraint->initially_valid = true;
10913  for (int i = 0; i < numfks; i++)
10914  {
10915  Form_pg_attribute att;
10916 
10917  att = TupleDescAttr(RelationGetDescr(partRel),
10918  mapped_conkey[i] - 1);
10919  fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
10920  makeString(NameStr(att->attname)));
10921  }
10923  RelationGetRelid(partRel),
10924  NameStr(constrForm->conname)))
10925  fkconstraint->conname =
10928  "fkey",
10929  RelationGetNamespace(partRel), NIL);
10930  else
10931  fkconstraint->conname = pstrdup(NameStr(constrForm->conname));
10932 
10933  indexOid = constrForm->conindid;
10934  constrOid =
10935  CreateConstraintEntry(fkconstraint->conname,
10936  constrForm->connamespace,
10937  CONSTRAINT_FOREIGN,
10938  fkconstraint->deferrable,
10939  fkconstraint->initdeferred,
10940  constrForm->convalidated,
10941  parentConstrOid,
10942  RelationGetRelid(partRel),
10943  mapped_conkey,
10944  numfks,
10945  numfks,
10946  InvalidOid, /* not a domain constraint */
10947  indexOid,
10948  constrForm->confrelid, /* same foreign rel */
10949  confkey,
10950  conpfeqop,
10951  conppeqop,
10952  conffeqop,
10953  numfks,
10954  fkconstraint->fk_upd_action,
10955  fkconstraint->fk_del_action,
10956  confdelsetcols,
10957  numfkdelsetcols,
10958  fkconstraint->fk_matchtype,
10959  NULL,
10960  NULL,
10961  NULL,
10962  false, /* islocal */
10963  1, /* inhcount */
10964  false, /* conNoInherit */
10965  true);
10966 
10967  /* Set up partition dependencies for the new constraint */
10968  ObjectAddressSet(address, ConstraintRelationId, constrOid);
10969  ObjectAddressSet(referenced, ConstraintRelationId, parentConstrOid);
10970  recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_PRI);
10971  ObjectAddressSet(referenced, RelationRelationId,
10972  RelationGetRelid(partRel));
10973  recordDependencyOn(&address, &referenced, DEPENDENCY_PARTITION_SEC);
10974 
10975  /* Done with the cloned constraint's tuple */
10976  ReleaseSysCache(tuple);
10977 
10978  /* Make all this visible before recursing */
10980 
10981  addFkRecurseReferencing(wqueue,
10982  fkconstraint,
10983  partRel,
10984  pkrel,
10985  indexOid,
10986  constrOid,
10987  numfks,
10988  confkey,
10989  mapped_conkey,
10990  conpfeqop,
10991  conppeqop,
10992  conffeqop,
10993  numfkdelsetcols,
10994  confdelsetcols,
10995  false, /* no old check exists */
10997  insertTriggerOid,
10998  updateTriggerOid);
10999  table_close(pkrel, NoLock);
11000  }
11001 
11002  table_close(trigrel, RowExclusiveLock);
11003 }
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
Definition: tablecmds.c:11221

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

10515 {
10516  /* This only works for declarative partitioning */
10517  Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
10518 
10519  /*
10520  * Clone constraints for which the parent is on the referenced side.
10521  */
10522  CloneFkReferenced(parentRel, partitionRel);
10523 
10524  /*
10525  * Now clone constraints where the parent is on the referencing side.
10526  */
10527  CloneFkReferencing(wqueue, parentRel, partitionRel);
10528 }
static void CloneFkReferencing(List **wqueue, Relation parentRel, Relation partRel)
Definition: tablecmds.c:10749
static void CloneFkReferenced(Relation parentRel, Relation partitionRel)
Definition: tablecmds.c:10544

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

18831 {
18832  Relation pg_trigger;
18833  ScanKeyData key;
18834  SysScanDesc scan;
18835  HeapTuple tuple;
18836  MemoryContext perTupCxt;
18837 
18838  ScanKeyInit(&key, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
18839  F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parent)));
18840  pg_trigger = table_open(TriggerRelationId, RowExclusiveLock);
18841  scan = systable_beginscan(pg_trigger, TriggerRelidNameIndexId,
18842  true, NULL, 1, &key);
18843 
18845  "clone trig", ALLOCSET_SMALL_SIZES);
18846 
18847  while (HeapTupleIsValid(tuple = systable_getnext(scan)))
18848  {
18849  Form_pg_trigger trigForm = (Form_pg_trigger) GETSTRUCT(tuple);
18850  CreateTrigStmt *trigStmt;
18851  Node *qual = NULL;
18852  Datum value;
18853  bool isnull;
18854  List *cols = NIL;
18855  List *trigargs = NIL;
18856  MemoryContext oldcxt;
18857 
18858  /*
18859  * Ignore statement-level triggers; those are not cloned.
18860  */
18861  if (!TRIGGER_FOR_ROW(trigForm->tgtype))
18862  continue;
18863 
18864  /*
18865  * Don't clone internal triggers, because the constraint cloning code
18866  * will.
18867  */
18868  if (trigForm->tgisinternal)
18869  continue;
18870 
18871  /*
18872  * Complain if we find an unexpected trigger type.
18873  */
18874  if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
18875  !TRIGGER_FOR_AFTER(trigForm->tgtype))
18876  elog(ERROR, "unexpected trigger \"%s\" found",
18877  NameStr(trigForm->tgname));
18878 
18879  /* Use short-lived context for CREATE TRIGGER */
18880  oldcxt = MemoryContextSwitchTo(perTupCxt);
18881 
18882  /*
18883  * If there is a WHEN clause, generate a 'cooked' version of it that's
18884  * appropriate for the partition.
18885  */
18886  value = heap_getattr(tuple, Anum_pg_trigger_tgqual,
18887  RelationGetDescr(pg_trigger), &isnull);
18888  if (!isnull)
18889  {
18891  qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
18892  partition, parent);
18893  qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
18894  partition, parent);
18895  }
18896 
18897  /*
18898  * If there is a column list, transform it to a list of column names.
18899  * Note we don't need to map this list in any way ...
18900  */
18901  if (trigForm->tgattr.dim1 > 0)
18902  {
18903  int i;
18904 
18905  for (i = 0; i < trigForm->tgattr.dim1; i++)
18906  {
18907  Form_pg_attribute col;
18908 
18909  col = TupleDescAttr(parent->rd_att,
18910  trigForm->tgattr.values[i] - 1);
18911  cols = lappend(cols,
18912  makeString(pstrdup(NameStr(col->attname))));
18913  }
18914  }
18915 
18916  /* Reconstruct trigger arguments list. */
18917  if (trigForm->tgnargs > 0)
18918  {
18919  char *p;
18920 
18921  value = heap_getattr(tuple, Anum_pg_trigger_tgargs,
18922  RelationGetDescr(pg_trigger), &isnull);
18923  if (isnull)
18924  elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
18925  NameStr(trigForm->tgname), RelationGetRelationName(partition));
18926 
18927  p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
18928 
18929  for (int i = 0; i < trigForm->tgnargs; i++)
18930  {
18931  trigargs = lappend(trigargs, makeString(pstrdup(p)));
18932  p += strlen(p) + 1;
18933  }
18934  }
18935 
18936  trigStmt = makeNode(CreateTrigStmt);
18937  trigStmt->replace = false;
18938  trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
18939  trigStmt->trigname = NameStr(trigForm->tgname);
18940  trigStmt->relation = NULL;
18941  trigStmt->funcname = NULL; /* passed separately */
18942  trigStmt->args = trigargs;
18943  trigStmt->row = true;
18944  trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
18945  trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
18946  trigStmt->columns = cols;
18947  trigStmt->whenClause = NULL; /* passed separately */
18948  trigStmt->transitionRels = NIL; /* not supported at present */
18949  trigStmt->deferrable = trigForm->tgdeferrable;
18950  trigStmt->initdeferred = trigForm->tginitdeferred;
18951  trigStmt->constrrel = NULL; /* passed separately */
18952 
18953  CreateTriggerFiringOn(trigStmt, NULL, RelationGetRelid(partition),
18954  trigForm->tgconstrrelid, InvalidOid, InvalidOid,
18955  trigForm->tgfoid, trigForm->oid, qual,
18956  false, true, trigForm->tgenabled);
18957 
18958  MemoryContextSwitchTo(oldcxt);
18959  MemoryContextReset(perTupCxt);
18960  }
18961 
18962  MemoryContextDelete(perTupCxt);
18963 
18964  systable_endscan(scan);
18965  table_close(pg_trigger, RowExclusiveLock);
18966 }
#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:3021
List * transitionRels
Definition: parsenodes.h:3023
RangeVar * constrrel
Definition: parsenodes.h:3027
RangeVar * relation
Definition: parsenodes.h:3012
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 17876 of file tablecmds.c.

17879 {
17880  int attn;
17881  ListCell *lc;
17882  Oid am_oid;
17883 
17884  attn = 0;
17885  foreach(lc, partParams)
17886  {
17887  PartitionElem *pelem = lfirst_node(PartitionElem, lc);
17888  Oid atttype;
17889  Oid attcollation;
17890 
17891  if (pelem->name != NULL)
17892  {
17893  /* Simple attribute reference */
17894  HeapTuple atttuple;
17895  Form_pg_attribute attform;
17896 
17897  atttuple = SearchSysCacheAttName(RelationGetRelid(rel),
17898  pelem->name);
17899  if (!HeapTupleIsValid(atttuple))
17900  ereport(ERROR,
17901  (errcode(ERRCODE_UNDEFINED_COLUMN),
17902  errmsg("column \"%s\" named in partition key does not exist",
17903  pelem->name),
17904  parser_errposition(pstate, pelem->location)));
17905  attform = (Form_pg_attribute) GETSTRUCT(atttuple);
17906 
17907  if (attform->attnum <= 0)
17908  ereport(ERROR,
17909  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17910  errmsg("cannot use system column \"%s\" in partition key",
17911  pelem->name),
17912  parser_errposition(pstate, pelem->location)));
17913 
17914  /*
17915  * Generated columns cannot work: They are computed after BEFORE
17916  * triggers, but partition routing is done before all triggers.
17917  */
17918  if (attform->attgenerated)
17919  ereport(ERROR,
17920  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17921  errmsg("cannot use generated column in partition key"),
17922  errdetail("Column \"%s\" is a generated column.",
17923  pelem->name),
17924  parser_errposition(pstate, pelem->location)));
17925 
17926  partattrs[attn] = attform->attnum;
17927  atttype = attform->atttypid;
17928  attcollation = attform->attcollation;
17929  ReleaseSysCache(atttuple);
17930  }
17931  else
17932  {
17933  /* Expression */
17934  Node *expr = pelem->expr;
17935  char partattname[16];
17936 
17937  Assert(expr != NULL);
17938  atttype = exprType(expr);
17939  attcollation = exprCollation(expr);
17940 
17941  /*
17942  * The expression must be of a storable type (e.g., not RECORD).
17943  * The test is the same as for whether a table column is of a safe
17944  * type (which is why we needn't check for the non-expression
17945  * case).
17946  */
17947  snprintf(partattname, sizeof(partattname), "%d", attn + 1);
17948  CheckAttributeType(partattname,
17949  atttype, attcollation,
17951 
17952  /*
17953  * Strip any top-level COLLATE clause. This ensures that we treat
17954  * "x COLLATE y" and "(x COLLATE y)" alike.
17955  */
17956  while (IsA(expr, CollateExpr))
17957  expr = (Node *) ((CollateExpr *) expr)->arg;
17958 
17959  if (IsA(expr, Var) &&
17960  ((Var *) expr)->varattno > 0)
17961  {
17962  /*
17963  * User wrote "(column)" or "(column COLLATE something)".
17964  * Treat it like simple attribute anyway.
17965  */
17966  partattrs[attn] = ((Var *) expr)->varattno;
17967  }
17968  else
17969  {
17970  Bitmapset *expr_attrs = NULL;
17971  int i;
17972 
17973  partattrs[attn] = 0; /* marks the column as expression */
17974  *partexprs = lappend(*partexprs, expr);
17975 
17976  /*
17977  * transformPartitionSpec() should have already rejected
17978  * subqueries, aggregates, window functions, and SRFs, based
17979  * on the EXPR_KIND_ for partition expressions.
17980  */
17981 
17982  /*
17983  * Cannot allow system column references, since that would
17984  * make partition routing impossible: their values won't be
17985  * known yet when we need to do that.
17986  */
17987  pull_varattnos(expr, 1, &expr_attrs);
17988  for (i = FirstLowInvalidHeapAttributeNumber; i < 0; i++)
17989  {
17991  expr_attrs))
17992  ereport(ERROR,
17993  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17994  errmsg("partition key expressions cannot contain system column references")));
17995  }
17996 
17997  /*
17998  * Generated columns cannot work: They are computed after
17999  * BEFORE triggers, but partition routing is done before all
18000  * triggers.
18001  */
18002  i = -1;
18003  while ((i = bms_next_member(expr_attrs, i)) >= 0)
18004  {
18006 
18007  if (attno > 0 &&
18008  TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
18009  ereport(ERROR,
18010  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18011  errmsg("cannot use generated column in partition key"),
18012  errdetail("Column \"%s\" is a generated column.",
18013  get_attname(RelationGetRelid(rel), attno, false)),
18014  parser_errposition(pstate, pelem->location)));
18015  }
18016 
18017  /*
18018  * Preprocess the expression before checking for mutability.
18019  * This is essential for the reasons described in
18020  * contain_mutable_functions_after_planning. However, we call
18021  * expression_planner for ourselves rather than using that
18022  * function, because if constant-folding reduces the
18023  * expression to a constant, we'd like to know that so we can
18024  * complain below.
18025  *
18026  * Like contain_mutable_functions_after_planning, assume that
18027  * expression_planner won't scribble on its input, so this
18028  * won't affect the partexprs entry we saved above.
18029  */
18030  expr = (Node *) expression_planner((Expr *) expr);
18031 
18032  /*
18033  * Partition expressions cannot contain mutable functions,
18034  * because a given row must always map to the same partition
18035  * as long as there is no change in the partition boundary
18036  * structure.
18037  */
18038  if (contain_mutable_functions(expr))
18039  ereport(ERROR,
18040  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18041  errmsg("functions in partition key expression must be marked IMMUTABLE")));
18042 
18043  /*
18044  * While it is not exactly *wrong* for a partition expression
18045  * to be a constant, it seems better to reject such keys.
18046  */
18047  if (IsA(expr, Const))
18048  ereport(ERROR,
18049  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
18050  errmsg("cannot use constant expression as partition key")));
18051  }
18052  }
18053 
18054  /*
18055  * Apply collation override if any
18056  */
18057  if (pelem->collation)
18058  attcollation = get_collation_oid(pelem->collation, false);
18059 
18060  /*
18061  * Check we have a collation iff it's a collatable type. The only
18062  * expected failures here are (1) COLLATE applied to a noncollatable
18063  * type, or (2) partition expression had an unresolved collation. But
18064  * we might as well code this to be a complete consistency check.
18065  */
18066  if (type_is_collatable(atttype))
18067  {
18068  if (!OidIsValid(attcollation))
18069  ereport(ERROR,
18070  (errcode(ERRCODE_INDETERMINATE_COLLATION),
18071  errmsg("could not determine which collation to use for partition expression"),
18072  errhint("Use the COLLATE clause to set the collation explicitly.")));
18073  }
18074  else
18075  {
18076  if (OidIsValid(attcollation))
18077  ereport(ERROR,
18078  (errcode(ERRCODE_DATATYPE_MISMATCH),
18079  errmsg("collations are not supported by type %s",
18080  format_type_be(atttype))));
18081  }
18082 
18083  partcollation[attn] = attcollation;
18084 
18085  /*
18086  * Identify the appropriate operator class. For list and range
18087  * partitioning, we use a btree operator class; hash partitioning uses
18088  * a hash operator class.
18089  */
18090  if (strategy == PARTITION_STRATEGY_HASH)
18091  am_oid = HASH_AM_OID;
18092  else
18093  am_oid = BTREE_AM_OID;
18094 
18095  if (!pelem->opclass)
18096  {
18097  partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
18098 
18099  if (!OidIsValid(partopclass[attn]))
18100  {
18101  if (strategy == PARTITION_STRATEGY_HASH)
18102  ereport(ERROR,
18103  (errcode(ERRCODE_UNDEFINED_OBJECT),
18104  errmsg("data type %s has no default operator class for access method \"%s\"",
18105  format_type_be(atttype), "hash"),
18106  errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
18107  else
18108  ereport(ERROR,
18109  (errcode(ERRCODE_UNDEFINED_OBJECT),
18110  errmsg("data type %s has no default operator class for access method \"%s\"",
18111  format_type_be(atttype), "btree"),
18112  errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
18113  }
18114  }
18115  else
18116  partopclass[attn] = ResolveOpClass(pelem->opclass,
18117  atttype,
18118  am_oid == HASH_AM_OID ? "hash" : "btree",
18119  am_oid);
18120 
18121  attn++;
18122  }
18123 }
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1306
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:510
bool contain_mutable_functions(Node *clause)
Definition: clauses.c:370
#define CHKATYPE_IS_PARTKEY
Definition: heap.h:25
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition: indexcmds.c:2304
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition: indexcmds.c:2219
bool type_is_collatable(Oid typid)
Definition: lsyscache.c:3081
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition: lsyscache.c:827
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 18187 of file tablecmds.c.

18188 {
18189  List *existConstraint = list_copy(provenConstraint);
18190  TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18191  int num_check,
18192  i;
18193 
18194  num_check = (constr != NULL) ? constr->num_check : 0;
18195  for (i = 0; i < num_check; i++)
18196  {
18197  Node *cexpr;
18198 
18199  /*
18200  * If this constraint hasn't been fully validated yet, we must ignore
18201  * it here.
18202  */
18203  if (!constr->check[i].ccvalid)
18204  continue;
18205 
18206  cexpr = stringToNode(constr->check[i].ccbin);
18207 
18208  /*
18209  * Run each expression through const-simplification and
18210  * canonicalization. It is necessary, because we will be comparing it
18211  * to similarly-processed partition constraint expressions, and may
18212  * fail to detect valid matches without this.
18213  */
18214  cexpr = eval_const_expressions(NULL, cexpr);
18215  cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
18216 
18217  existConstraint = list_concat(existConstraint,
18218  make_ands_implicit((Expr *) cexpr));
18219  }
18220 
18221  /*
18222  * Try to make the proof. Since we are comparing CHECK constraints, we
18223  * need to use weak implication, i.e., we assume existConstraint is
18224  * not-false and try to prove the same for testConstraint.
18225  *
18226  * Note that predicate_implied_by assumes its first argument is known
18227  * immutable. That should always be true for both NOT NULL and partition
18228  * constraints, so we don't test it here.
18229  */
18230  return predicate_implied_by(testConstraint, existConstraint, true);
18231 }
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
ConstrCheck * check
Definition: tupdesc.h:40
uint16 num_check
Definition: tupdesc.h:43

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

15705 {
15708 
15709  if (acon->condeferrable != bcon->condeferrable ||
15710  acon->condeferred != bcon->condeferred ||
15711  strcmp(decompile_conbin(a, tupleDesc),
15712  decompile_conbin(b, tupleDesc)) != 0)
15713  return false;
15714  else
15715  return true;
15716 }
int b
Definition: isn.c:70
int a
Definition: isn.c:69
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)
Definition: tablecmds.c:15679

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

12213 {
12214  ObjectAddress trigAddress;
12215  CreateTrigStmt *fk_trigger;
12216 
12217  /*
12218  * Note: for a self-referential FK (referencing and referenced tables are
12219  * the same), it is important that the ON UPDATE action fires before the
12220  * CHECK action, since both triggers will fire on the same row during an
12221  * UPDATE event; otherwise the CHECK trigger will be checking a non-final
12222  * state of the row. Triggers fire in name order, so we ensure this by
12223  * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
12224  * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
12225  */
12226  fk_trigger = makeNode(CreateTrigStmt);
12227  fk_trigger->replace = false;
12228  fk_trigger->isconstraint = true;
12229  fk_trigger->trigname = "RI_ConstraintTrigger_c";
12230  fk_trigger->relation = NULL;
12231 
12232  /* Either ON INSERT or ON UPDATE */
12233  if (on_insert)
12234  {
12235  fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
12236  fk_trigger->events = TRIGGER_TYPE_INSERT;
12237  }
12238  else
12239  {
12240  fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
12241  fk_trigger->events = TRIGGER_TYPE_UPDATE;
12242  }
12243 
12244  fk_trigger->args = NIL;
12245  fk_trigger->row = true;
12246  fk_trigger->timing = TRIGGER_TYPE_AFTER;
12247  fk_trigger->columns = NIL;
12248  fk_trigger->whenClause = NULL;
12249  fk_trigger->transitionRels = NIL;
12250  fk_trigger->deferrable = fkconstraint->deferrable;
12251  fk_trigger->initdeferred = fkconstraint->initdeferred;
12252  fk_trigger->constrrel = NULL;
12253 
12254  trigAddress = CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid,
12255  constraintOid, indexOid, InvalidOid,
12256  parentTrigOid, NULL, true, false);
12257 
12258  /* Make changes-so-far visible */
12260 
12261  return trigAddress.objectId;
12262 }
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 12273 of file tablecmds.c.

12277 {
12278  CreateTrigStmt *fk_trigger;
12279  ObjectAddress trigAddress;
12280 
12281  /*
12282  * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12283  * DELETE action on the referenced table.
12284  */
12285  fk_trigger = makeNode(CreateTrigStmt);
12286  fk_trigger->replace = false;
12287  fk_trigger->isconstraint = true;
12288  fk_trigger->trigname = "RI_ConstraintTrigger_a";
12289  fk_trigger->relation = NULL;
12290  fk_trigger->args = NIL;
12291  fk_trigger->row = true;
12292  fk_trigger->timing = TRIGGER_TYPE_AFTER;
12293  fk_trigger->events = TRIGGER_TYPE_DELETE;
12294  fk_trigger->columns = NIL;
12295  fk_trigger->whenClause = NULL;
12296  fk_trigger->transitionRels = NIL;
12297  fk_trigger->constrrel = NULL;
12298  switch (fkconstraint->fk_del_action)
12299  {
12301  fk_trigger->deferrable = fkconstraint->deferrable;
12302  fk_trigger->initdeferred = fkconstraint->initdeferred;
12303  fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
12304  break;
12306  fk_trigger->deferrable = false;
12307  fk_trigger->initdeferred = false;
12308  fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
12309  break;
12311  fk_trigger->deferrable = false;
12312  fk_trigger->initdeferred = false;
12313  fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
12314  break;
12316  fk_trigger->deferrable = false;
12317  fk_trigger->initdeferred = false;
12318  fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
12319  break;
12321  fk_trigger->deferrable = false;
12322  fk_trigger->initdeferred = false;
12323  fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
12324  break;
12325  default:
12326  elog(ERROR, "unrecognized FK action type: %d",
12327  (int) fkconstraint->fk_del_action);
12328  break;
12329  }
12330 
12331  trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12332  RelationGetRelid(rel),
12333  constraintOid, indexOid, InvalidOid,
12334  parentDelTrigger, NULL, true, false);
12335  if (deleteTrigOid)
12336  *deleteTrigOid = trigAddress.objectId;
12337 
12338  /* Make changes-so-far visible */
12340 
12341  /*
12342  * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
12343  * UPDATE action on the referenced table.
12344  */
12345  fk_trigger = makeNode(CreateTrigStmt);
12346  fk_trigger->replace = false;
12347  fk_trigger->isconstraint = true;
12348  fk_trigger->trigname = "RI_ConstraintTrigger_a";
12349  fk_trigger->relation = NULL;
12350  fk_trigger->args = NIL;
12351  fk_trigger->row = true;
12352  fk_trigger->timing = TRIGGER_TYPE_AFTER;
12353  fk_trigger->events = TRIGGER_TYPE_UPDATE;
12354  fk_trigger->columns = NIL;
12355  fk_trigger->whenClause = NULL;
12356  fk_trigger->transitionRels = NIL;
12357  fk_trigger->constrrel = NULL;
12358  switch (fkconstraint->fk_upd_action)
12359  {
12361  fk_trigger->deferrable = fkconstraint->deferrable;
12362  fk_trigger->initdeferred = fkconstraint->initdeferred;
12363  fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
12364  break;
12366  fk_trigger->deferrable = false;
12367  fk_trigger->initdeferred = false;
12368  fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
12369  break;
12371  fk_trigger->deferrable = false;
12372  fk_trigger->initdeferred = false;
12373  fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
12374  break;
12376  fk_trigger->deferrable = false;
12377  fk_trigger->initdeferred = false;
12378  fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
12379  break;
12381  fk_trigger->deferrable = false;
12382  fk_trigger->initdeferred = false;
12383  fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
12384  break;
12385  default:
12386  elog(ERROR, "unrecognized FK action type: %d",
12387  (int) fkconstraint->fk_upd_action);
12388  break;
12389  }
12390 
12391  trigAddress = CreateTrigger(fk_trigger, NULL, refRelOid,
12392  RelationGetRelid(rel),
12393  constraintOid, indexOid, InvalidOid,
12394  parentUpdTrigger, NULL, true, false);
12395  if (updateTrigOid)
12396  *updateTrigOid = trigAddress.objectId;
12397 }
#define FKCONSTR_ACTION_RESTRICT
Definition: parsenodes.h:2723
#define FKCONSTR_ACTION_NOACTION
Definition: parsenodes.h:2722

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

12413 {
12414  *insertTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12415  constraintOid, indexOid,
12416  parentInsTrigger, true);
12417  *updateTrigOid = CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint,
12418  constraintOid, indexOid,
12419  parentUpdTrigger, false);
12420 }
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
Definition: tablecmds.c:12210

References CreateFKCheckTrigger().

Referenced by addFkRecurseReferencing().

◆ CreateInheritance()

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

Definition at line 15610 of file tablecmds.c.

15611 {
15612  Relation catalogRelation;
15613  SysScanDesc scan;
15614  ScanKeyData key;
15615  HeapTuple inheritsTuple;
15616  int32 inhseqno;
15617 
15618  /* Note: get RowExclusiveLock because we will write pg_inherits below. */
15619  catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
15620 
15621  /*
15622  * Check for duplicates in the list of parents, and determine the highest
15623  * inhseqno already present; we'll use the next one for the new parent.
15624  * Also, if proposed child is a partition, it cannot already be
15625  * inheriting.
15626  *
15627  * Note: we do not reject the case where the child already inherits from
15628  * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
15629  */
15630  ScanKeyInit(&key,
15631  Anum_pg_inherits_inhrelid,
15632  BTEqualStrategyNumber, F_OIDEQ,
15633  ObjectIdGetDatum(RelationGetRelid(child_rel)));
15634  scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
15635  true, NULL, 1, &key);
15636 
15637  /* inhseqno sequences start at 1 */
15638  inhseqno = 0;
15639  while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
15640  {
15641  Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
15642 
15643  if (inh->inhparent == RelationGetRelid(parent_rel))
15644  ereport(ERROR,
15645  (errcode(ERRCODE_DUPLICATE_TABLE),
15646  errmsg("relation \"%s\" would be inherited from more than once",
15647  RelationGetRelationName(parent_rel))));
15648 
15649  if (inh->inhseqno > inhseqno)
15650  inhseqno = inh->inhseqno;
15651  }
15652  systable_endscan(scan);
15653 
15654  /* Match up the columns and bump attinhcount as needed */
15655  MergeAttributesIntoExisting(child_rel, parent_rel, ispartition);
15656 
15657  /* Match up the constraints and bump coninhcount as needed */
15658  MergeConstraintsIntoExisting(child_rel, parent_rel);
15659 
15660  /*
15661  * OK, it looks valid. Make the catalog entries that show inheritance.
15662  */
15664  RelationGetRelid(parent_rel),
15665  inhseqno + 1,
15666  catalogRelation,
15667  parent_rel->rd_rel->relkind ==
15668  RELKIND_PARTITIONED_TABLE);
15669 
15670  /* Now we're done with pg_inherits */
15671  table_close(catalogRelation, RowExclusiveLock);
15672 }
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:15733
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition: tablecmds.c:3402
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
Definition: tablecmds.c:15853

References BTEqualStrategyNumber, ereport, errcode(), errmsg(), ERROR, GETSTRUCT, HeapTupleIsValid, sort-test::key, MergeAttributesIntoExisting(), MergeConstraintsIntoExisting(), ObjectIdGetDatum(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), StoreCatalogInheritance1(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecAddInherit(), and attachPartitionTable().

◆ createPartitionTable()

static Relation createPartitionTable ( RangeVar newPartName,
Relation  modelRel,
AlterTableUtilityContext context 
)
static

Definition at line 20325 of file tablecmds.c.

20327 {
20328  CreateStmt *createStmt;
20329  TableLikeClause *tlc;
20330  PlannedStmt *wrapper;
20331  Relation newRel;
20332 
20333  /* If existing rel is temp, it must belong to this session */
20334  if (modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20335  !modelRel->rd_islocaltemp)
20336  ereport(ERROR,
20337  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20338  errmsg("cannot create as partition of temporary relation of another session")));
20339 
20340  /* New partition should have the same persistence as modelRel */
20341  newPartName->relpersistence = modelRel->rd_rel->relpersistence;
20342 
20343  createStmt = makeNode(CreateStmt);
20344  createStmt->relation = newPartName;
20345  createStmt->tableElts = NIL;
20346  createStmt->inhRelations = NIL;
20347  createStmt->constraints = NIL;
20348  createStmt->options = NIL;
20349  createStmt->oncommit = ONCOMMIT_NOOP;
20350  createStmt->tablespacename = get_tablespace_name(modelRel->rd_rel->reltablespace);
20351  createStmt->if_not_exists = false;
20352  createStmt->accessMethod = get_am_name(modelRel->rd_rel->relam);
20353 
20354  tlc = makeNode(TableLikeClause);
20356  RelationGetRelationName(modelRel), -1);
20357 
20358  /*
20359  * Indexes will be inherited on "attach new partitions" stage, after data
20360  * moving. We also don't copy the extended statistics for consistency
20361  * with CREATE TABLE PARTITION OF.
20362  */
20365  tlc->relationOid = InvalidOid;
20366  createStmt->tableElts = lappend(createStmt->tableElts, tlc);
20367 
20368  /* Need to make a wrapper PlannedStmt. */
20369  wrapper = makeNode(PlannedStmt);
20370  wrapper->commandType = CMD_UTILITY;
20371  wrapper->canSetTag = false;
20372  wrapper->utilityStmt = (Node *) createStmt;
20373  wrapper->stmt_location = context->pstmt->stmt_location;
20374  wrapper->stmt_len = context->pstmt->stmt_len;
20375 
20376  ProcessUtility(wrapper,
20377  context->queryString,
20378  false,
20380  NULL,
20381  NULL,
20382  None_Receiver,
20383  NULL);
20384 
20385  /*
20386  * Open the new partition with no lock, because we already have
20387  * AccessExclusiveLock placed there after creation.
20388  */
20389  newRel = table_openrv(newPartName, NoLock);
20390 
20391  /*
20392  * We intended to create the partition with the same persistence as the
20393  * parent table, but we still need to recheck because that might be
20394  * affected by the search_path. If the parent is permanent, so must be
20395  * all of its partitions.
20396  */
20397  if (modelRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20398  newRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20399  ereport(ERROR,
20400  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20401  errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
20402  RelationGetRelationName(modelRel))));
20403 
20404  /* Permanent rels cannot be partitions belonging to temporary parent */
20405  if (newRel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20406  modelRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20407  ereport(ERROR,
20408  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
20409  errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
20410  RelationGetRelationName(modelRel))));
20411 
20412  return newRel;
20413 }
char * get_am_name(Oid amOid)
Definition: amcmds.c:192
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
@ CREATE_TABLE_LIKE_STATISTICS
Definition: parsenodes.h:768
@ ONCOMMIT_NOOP
Definition: primnodes.h:57
List * tableElts
Definition: parsenodes.h:2657
OnCommitAction oncommit
Definition: parsenodes.h:2665
List * options
Definition: parsenodes.h:2664
bool if_not_exists
Definition: parsenodes.h:2668
List * inhRelations
Definition: parsenodes.h:2658
RangeVar * relation
Definition: parsenodes.h:2656
char * tablespacename
Definition: parsenodes.h:2666
char * accessMethod
Definition: parsenodes.h:2667
List * constraints
Definition: parsenodes.h:2663
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 CreateStmt::accessMethod, PlannedStmt::canSetTag, CMD_UTILITY, PlannedStmt::commandType, CreateStmt::constraints, context, CREATE_TABLE_LIKE_ALL, CREATE_TABLE_LIKE_IDENTITY, CREATE_TABLE_LIKE_INDEXES, CREATE_TABLE_LIKE_STATISTICS, ereport, errcode(), errmsg(), ERROR, get_am_name(), get_namespace_name(), get_tablespace_name(), CreateStmt::if_not_exists, CreateStmt::inhRelations, InvalidOid, lappend(), makeNode, makeRangeVar(), NIL, NoLock, None_Receiver, CreateStmt::oncommit, ONCOMMIT_NOOP, TableLikeClause::options, CreateStmt::options, PROCESS_UTILITY_SUBCOMMAND, ProcessUtility(), RelationData::rd_islocaltemp, RelationData::rd_rel, TableLikeClause::relation, CreateStmt::relation, RelationGetNamespace, RelationGetRelationName, TableLikeClause::relationOid, RangeVar::relpersistence, PlannedStmt::stmt_len, PlannedStmt::stmt_location, table_openrv(), CreateStmt::tableElts, CreateStmt::tablespacename, and PlannedStmt::utilityStmt.

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ createSplitPartitionContext()

static SplitPartitionContext* createSplitPartitionContext ( Relation  partRel)
static

Definition at line 20078 of file tablecmds.c.

20079 {
20081 
20083  pc->partRel = partRel;
20084 
20085  /*
20086  * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
20087  * don't bother using it.
20088  */
20089  pc->bistate = GetBulkInsertState();
20090 
20091  /* Create tuple slot for new partition. */
20095 
20096  return pc;
20097 }
TupleTableSlot * dstslot
Definition: tablecmds.c:20069
BulkInsertState bistate
Definition: tablecmds.c:20068

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

15680 {
15681  Form_pg_constraint con;
15682  bool isnull;
15683  Datum attr;
15684  Datum expr;
15685 
15686  con = (Form_pg_constraint) GETSTRUCT(contup);
15687  attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
15688  if (isnull)
15689  elog(ERROR, "null conbin for constraint %u", con->oid);
15690 
15691  expr = DirectFunctionCall2(pg_get_expr, attr,
15692  ObjectIdGetDatum(con->conrelid));
15693  return TextDatumGetCString(expr);
15694 }
#define DirectFunctionCall2(func, arg1, arg2)
Definition: fmgr.h:644
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition: ruleutils.c:2628

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

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

References AccessExclusiveLock, AccessShareLock, ACL_CREATE, ACL_USAGE, aclcheck_error(), aclcheck_error_type(), ACLCHECK_OK, addNSItemToQuery(), addRangeTableEntryForRelation(), AddRelationNewConstraints(), 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_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, 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 19441 of file tablecmds.c.

19442 {
19443  List *constraintExpr;
19444 
19445  constraintExpr = RelationGetPartitionQual(partRel);
19446  constraintExpr = (List *) eval_const_expressions(NULL, (Node *) constraintExpr);
19447 
19448  /*
19449  * Avoid adding a new constraint if the needed constraint is implied by an
19450  * existing constraint
19451  */
19452  if (!PartConstraintImpliedByRelConstraint(partRel, constraintExpr))
19453  {
19454  AlteredTableInfo *tab;
19455  Constraint *n;
19456 
19457  tab = ATGetQueueEntry(wqueue, partRel);
19458 
19459  /* Add constraint on partition, equivalent to the partition constraint */
19460  n = makeNode(Constraint);
19461  n->contype = CONSTR_CHECK;
19462  n->conname = NULL;
19463  n->location = -1;
19464  n->is_no_inherit = false;
19465  n->raw_expr = NULL;
19466  n->cooked_expr = nodeToString(make_ands_explicit(constraintExpr));
19467  n->initially_valid = true;
19468  n->skip_validation = true;
19469  /* It's a re-add, since it nominally already exists */
19470  ATAddCheckConstraint(wqueue, tab, partRel, n,
19471  true, false, true, ShareUpdateExclusiveLock);
19472  }
19473 }
char * nodeToString(const void *obj)
Definition: outfuncs.c:791
char * cooked_expr
Definition: parsenodes.h:2745
Node * raw_expr
Definition: parsenodes.h:2743
bool PartConstraintImpliedByRelConstraint(Relation scanrel, List *partConstraint)
Definition: tablecmds.c:18134

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

19160 {
19161  Relation classRel;
19162  List *fks;
19163  ListCell *cell;
19164  List *indexes;
19165  Datum new_val[Natts_pg_class];
19166  bool new_null[Natts_pg_class],
19167  new_repl[Natts_pg_class];
19168  HeapTuple tuple,
19169  newtuple;
19170  Relation trigrel = NULL;
19171 
19172  if (concurrent)
19173  {
19174  /*
19175  * We can remove the pg_inherits row now. (In the non-concurrent case,
19176  * this was already done).
19177  */
19178  RemoveInheritance(partRel, rel, true);
19179  }
19180 
19181  /* Drop any triggers that were cloned on creation/attach. */
19183 
19184  /*
19185  * Detach any foreign keys that are inherited. This includes creating
19186  * additional action triggers.
19187  */
19188  fks = copyObject(RelationGetFKeyList(partRel));
19189  if (fks != NIL)
19190  trigrel = table_open(TriggerRelationId, RowExclusiveLock);
19191  foreach(cell, fks)
19192  {
19193  ForeignKeyCacheInfo *fk = lfirst(cell);
19194  HeapTuple contup;
19195  Form_pg_constraint conform;
19196  Constraint *fkconstraint;
19197  Oid insertTriggerOid,
19198  updateTriggerOid;
19199 
19200  contup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(fk->conoid));
19201  if (!HeapTupleIsValid(contup))
19202  elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
19203  conform = (Form_pg_constraint) GETSTRUCT(contup);
19204 
19205  /* consider only the inherited foreign keys */
19206  if (conform->contype != CONSTRAINT_FOREIGN ||
19207  !OidIsValid(conform->conparentid))
19208  {
19209  ReleaseSysCache(contup);
19210  continue;
19211  }
19212 
19213  /* unset conparentid and adjust conislocal, coninhcount, etc. */
19215 
19216  /*
19217  * Also, look up the partition's "check" triggers corresponding to the
19218  * constraint being detached and detach them from the parent triggers.
19219  */
19221  fk->conoid, fk->confrelid, fk->conrelid,
19222  &insertTriggerOid, &updateTriggerOid);
19223  Assert(OidIsValid(insertTriggerOid));
19224  TriggerSetParentTrigger(trigrel, insertTriggerOid, InvalidOid,
19225  RelationGetRelid(partRel));
19226  Assert(OidIsValid(updateTriggerOid));
19227  TriggerSetParentTrigger(trigrel, updateTriggerOid, InvalidOid,
19228  RelationGetRelid(partRel));
19229 
19230  /*
19231  * Make the action triggers on the referenced relation. When this was
19232  * a partition the action triggers pointed to the parent rel (they
19233  * still do), but now we need separate ones of our own.
19234  */
19235  fkconstraint = makeNode(Constraint);
19236  fkconstraint->contype = CONSTRAINT_FOREIGN;
19237  fkconstraint->conname = pstrdup(NameStr(conform->conname));
19238  fkconstraint->deferrable = conform->condeferrable;
19239  fkconstraint->initdeferred = conform->condeferred;
19240  fkconstraint->location = -1;
19241  fkconstraint->pktable = NULL;
19242  fkconstraint->fk_attrs = NIL;
19243  fkconstraint->pk_attrs = NIL;
19244  fkconstraint->fk_matchtype = conform->confmatchtype;
19245  fkconstraint->fk_upd_action = conform->confupdtype;
19246  fkconstraint->fk_del_action = conform->confdeltype;
19247  fkconstraint->fk_del_set_cols = NIL;
19248  fkconstraint->old_conpfeqop = NIL;
19249  fkconstraint->old_pktable_oid = InvalidOid;
19250  fkconstraint->skip_validation = false;
19251  fkconstraint->initially_valid = true;
19252 
19253  createForeignKeyActionTriggers(partRel, conform->confrelid,
19254  fkconstraint, fk->conoid,
19255  conform->conindid,
19257  NULL, NULL);
19258 
19259  ReleaseSysCache(contup);
19260  }
19261  list_free_deep(fks);
19262  if (trigrel)
19263  table_close(trigrel, RowExclusiveLock);
19264 
19265  /*
19266  * Any sub-constraints that are in the referenced-side of a larger
19267  * constraint have to be removed. This partition is no longer part of the
19268  * key space of the constraint.
19269  */
19270  foreach(cell, GetParentedForeignKeyRefs(partRel))
19271  {
19272  Oid constrOid = lfirst_oid(cell);
19273  ObjectAddress constraint;
19274 
19276  deleteDependencyRecordsForClass(ConstraintRelationId,
19277  constrOid,
19278  ConstraintRelationId,
19281 
19282  ObjectAddressSet(constraint, ConstraintRelationId, constrOid);
19283  performDeletion(&constraint, DROP_RESTRICT, 0);
19284  }
19285 
19286  /* Now we can detach indexes */
19287  indexes = RelationGetIndexList(partRel);
19288  foreach(cell, indexes)
19289  {
19290  Oid idxid = lfirst_oid(cell);
19291  Oid parentidx;
19292  Relation idx;
19293  Oid constrOid;
19294  Oid parentConstrOid;
19295 
19296  if (!has_superclass(idxid))
19297  continue;
19298 
19299  parentidx = get_partition_parent(idxid, false);
19300  Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
19301 
19304 
19305  /*
19306  * If there's a constraint associated with the index, detach it too.
19307  * Careful: it is possible for a constraint index in a partition to be
19308  * the child of a non-constraint index, so verify whether the parent
19309  * index does actually have a constraint.
19310  */
19312  idxid);
19313  parentConstrOid = get_relation_idx_constraint_oid(RelationGetRelid(rel),
19314  parentidx);
19315  if (OidIsValid(parentConstrOid) && OidIsValid(constrOid))
19317 
19319  }
19320 
19321  /* Update pg_class tuple */
19322  classRel = table_open(RelationRelationId, RowExclusiveLock);
19323  tuple = SearchSysCacheCopy1(RELOID,
19325  if (!HeapTupleIsValid(tuple))
19326  elog(ERROR, "cache lookup failed for relation %u",
19327  RelationGetRelid(partRel));
19328  Assert(((Form_pg_class) GETSTRUCT(tuple))->relispartition);
19329 
19330  /* Clear relpartbound and reset relispartition */
19331  memset(new_val, 0, sizeof(new_val));
19332  memset(new_null, false, sizeof(new_null));
19333  memset(new_repl, false, sizeof(new_repl));
19334  new_val[Anum_pg_class_relpartbound - 1] = (Datum) 0;
19335  new_null[Anum_pg_class_relpartbound - 1] = true;
19336  new_repl[Anum_pg_class_relpartbound - 1] = true;
19337  newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
19338  new_val, new_null, new_repl);
19339 
19340  ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
19341  CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
19342  heap_freetuple(newtuple);
19343  table_close(classRel, RowExclusiveLock);
19344 
19345  /*
19346  * Drop identity property from all identity columns of partition.
19347  */
19348  for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
19349  {
19350  Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
19351 
19352  if (!attr->attisdropped && attr->attidentity)
19353  ATExecDropIdentity(partRel, NameStr(attr->attname), false,
19354  AccessExclusiveLock, true, true);
19355  }
19356 
19357  if (OidIsValid(defaultPartOid))
19358  {
19359  /*
19360  * If the relation being detached is the default partition itself,
19361  * remove it from the parent's pg_partitioned_table entry.
19362  *
19363  * If not, we must invalidate default partition's relcache entry, as
19364  * in StorePartitionBound: its partition constraint depends on every
19365  * other partition's partition constraint.
19366  */
19367  if (RelationGetRelid(partRel) == defaultPartOid)
19369  else
19370  CacheInvalidateRelcacheByRelid(defaultPartOid);
19371  }
19372 
19373  /*
19374  * Invalidate the parent's relcache so that the partition is no longer
19375  * included in its partition descriptor.
19376  */
19378 
19379  /*
19380  * If the partition we just detached is partitioned itself, invalidate
19381  * relcache for all descendent partitions too to ensure that their
19382  * rd_partcheck expression trees are rebuilt; must lock partitions before
19383  * doing so, using the same lockmode as what partRel has been locked with
19384  * by the caller.
19385  */
19386  if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
19387  {
19388  List *children;
19389 
19390  children = find_all_inheritors(RelationGetRelid(partRel),
19391  AccessExclusiveLock, NULL);
19392  foreach(cell, children)
19393  {
19395  }
19396  }
19397 }
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:19482
void TriggerSetParentTrigger(Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
Definition: trigger.c:1215

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

16273 {
16274  Relation catalogRelation;
16275  SysScanDesc scan;
16276  ScanKeyData key[3];
16277  HeapTuple depTuple;
16278 
16279  catalogRelation = table_open(DependRelationId, RowExclusiveLock);
16280 
16281  ScanKeyInit(&key[0],
16282  Anum_pg_depend_classid,
16283  BTEqualStrategyNumber, F_OIDEQ,
16284  ObjectIdGetDatum(RelationRelationId));
16285  ScanKeyInit(&key[1],
16286  Anum_pg_depend_objid,
16287  BTEqualStrategyNumber, F_OIDEQ,
16288  ObjectIdGetDatum(relid));
16289  ScanKeyInit(&key[2],
16290  Anum_pg_depend_objsubid,
16291  BTEqualStrategyNumber, F_INT4EQ,
16292  Int32GetDatum(0));
16293 
16294  scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
16295  NULL, 3, key);
16296 
16297  while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
16298  {
16299  Form_pg_depend dep = (Form_pg_depend) GETSTRUCT(depTuple);
16300 
16301  if (dep->refclassid == refclassid &&
16302  dep->refobjid == refobjid &&
16303  dep->refobjsubid == 0 &&
16304  dep->deptype == deptype)
16305  CatalogTupleDelete(catalogRelation, &depTuple->t_self);
16306  }
16307 
16308  systable_endscan(scan);
16309  table_close(catalogRelation, RowExclusiveLock);
16310 }

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

19483 {
19484  ScanKeyData skey;
19485  SysScanDesc scan;
19486  HeapTuple trigtup;
19487  Relation tgrel;
19488  ObjectAddresses *objects;
19489 
19490  objects = new_object_addresses();
19491 
19492  /*
19493  * Scan pg_trigger to search for all triggers on this rel.
19494  */
19495  ScanKeyInit(&skey, Anum_pg_trigger_tgrelid, BTEqualStrategyNumber,
19496  F_OIDEQ, ObjectIdGetDatum(partitionId));
19497  tgrel = table_open(TriggerRelationId, RowExclusiveLock);
19498  scan = systable_beginscan(tgrel, TriggerRelidNameIndexId,
19499  true, NULL, 1, &skey);
19500  while (HeapTupleIsValid(trigtup = systable_getnext(scan)))
19501  {
19502  Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(trigtup);
19503  ObjectAddress trig;
19504 
19505  /* Ignore triggers that weren't cloned */
19506  if (!OidIsValid(pg_trigger->tgparentid))
19507  continue;
19508 
19509  /*
19510  * Ignore internal triggers that are implementation objects of foreign
19511  * keys, because these will be detached when the foreign keys
19512  * themselves are.
19513  */
19514  if (OidIsValid(pg_trigger->tgconstrrelid))
19515  continue;
19516 
19517  /*
19518  * This is ugly, but necessary: remove the dependency markings on the
19519  * trigger so that it can be removed.
19520  */
19521  deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
19522  TriggerRelationId,
19524  deleteDependencyRecordsForClass(TriggerRelationId, pg_trigger->oid,
19525  RelationRelationId,
19527 
19528  /* remember this trigger to remove it below */
19529  ObjectAddressSet(trig, TriggerRelationId, pg_trigger->oid);
19530  add_exact_object_address(&trig, objects);
19531  }
19532 
19533  /* make the dependency removal visible to the deletion below */
19536 
19537  /* done */
19538  free_object_addresses(objects);
19539  systable_endscan(scan);
19540  table_close(tgrel, RowExclusiveLock);
19541 }

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

◆ DropErrorMsgNonExistent()

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

Definition at line 1362 of file tablecmds.c.

1363 {
1364  const struct dropmsgstrings *rentry;
1365 
1366  if (rel->schemaname != NULL &&
1368  {
1369  if (!missing_ok)
1370  {
1371  ereport(ERROR,
1372  (errcode(ERRCODE_UNDEFINED_SCHEMA),
1373  errmsg("schema \"%s\" does not exist", rel->schemaname)));
1374  }
1375  else
1376  {
1377  ereport(NOTICE,
1378  (errmsg("schema \"%s\" does not exist, skipping",
1379  rel->schemaname)));
1380  }
1381  return;
1382  }
1383 
1384  for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1385  {
1386  if (rentry->kind == rightkind)
1387  {
1388  if (!missing_ok)
1389  {
1390  ereport(ERROR,
1391  (errcode(rentry->nonexistent_code),
1392  errmsg(rentry->nonexistent_msg, rel->relname)));
1393  }
1394  else
1395  {
1396  ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1397  break;
1398  }
1399  }
1400  }
1401 
1402  Assert(rentry->kind != '\0'); /* Should be impossible */
1403 }
Oid LookupNamespaceNoError(const char *nspname)
Definition: namespace.c:3340
char * schemaname
Definition: primnodes.h:79
const char * skipping_msg
Definition: tablecmds.c:247
int nonexistent_code
Definition: tablecmds.c:245
const char * nonexistent_msg
Definition: tablecmds.c:246
static const struct dropmsgstrings dropmsgstringarray[]
Definition: tablecmds.c:252

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

1411 {
1412  const struct dropmsgstrings *rentry;
1413  const struct dropmsgstrings *wentry;
1414 
1415  for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1416  if (rentry->kind == rightkind)
1417  break;
1418  Assert(rentry->kind != '\0');
1419 
1420  for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1421  if (wentry->kind == wrongkind)
1422  break;
1423  /* wrongkind could be something we don't have in our table... */
1424 
1425  ereport(ERROR,
1426  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1427  errmsg(rentry->nota_msg, relname),
1428  (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1429 }
const char * drophint_msg
Definition: tablecmds.c:249
const char * nota_msg
Definition: tablecmds.c:248

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

1761 {
1762  List *rels = NIL;
1763  List *relids = NIL;
1764  List *relids_logged = NIL;
1765  ListCell *cell;
1766 
1767  /*
1768  * Open, exclusive-lock, and check all the explicitly-specified relations
1769  */
1770  foreach(cell, stmt->relations)
1771  {
1772  RangeVar *rv = lfirst(cell);
1773  Relation rel;
1774  bool recurse = rv->inh;
1775  Oid myrelid;
1776  LOCKMODE lockmode = AccessExclusiveLock;
1777 
1778  myrelid = RangeVarGetRelidExtended(rv, lockmode,
1780  NULL);
1781 
1782  /* don't throw error for "TRUNCATE foo, foo" */
1783  if (list_member_oid(relids, myrelid))
1784  continue;
1785 
1786  /* open the relation, we already hold a lock on it */
1787  rel = table_open(myrelid, NoLock);
1788 
1789  /*
1790  * RangeVarGetRelidExtended() has done most checks with its callback,
1791  * but other checks with the now-opened Relation remain.
1792  */
1794 
1795  rels = lappend(rels, rel);
1796  relids = lappend_oid(relids, myrelid);
1797 
1798  /* Log this relation only if needed for logical decoding */
1799  if (RelationIsLogicallyLogged(rel))
1800  relids_logged = lappend_oid(relids_logged, myrelid);
1801 
1802  if (recurse)
1803  {
1804  ListCell *child;
1805  List *children;
1806 
1807  children = find_all_inheritors(myrelid, lockmode, NULL);
1808 
1809  foreach(child, children)
1810  {
1811  Oid childrelid = lfirst_oid(child);
1812 
1813  if (list_member_oid(relids, childrelid))
1814  continue;
1815 
1816  /* find_all_inheritors already got lock */
1817  rel = table_open(childrelid, NoLock);
1818 
1819  /*
1820  * It is possible that the parent table has children that are
1821  * temp tables of other backends. We cannot safely access
1822  * such tables (because of buffering issues), and the best
1823  * thing to do is to silently ignore them. Note that this
1824  * check is the same as one of the checks done in
1825  * truncate_check_activity() called below, still it is kept
1826  * here for simplicity.
1827  */
1828  if (RELATION_IS_OTHER_TEMP(rel))
1829  {
1830  table_close(rel, lockmode);
1831  continue;
1832  }
1833 
1834  /*
1835  * Inherited TRUNCATE commands perform access permission
1836  * checks on the parent table only. So we skip checking the
1837  * children's permissions and don't call
1838  * truncate_check_perms() here.
1839  */
1842 
1843  rels = lappend(rels, rel);
1844  relids = lappend_oid(relids, childrelid);
1845 
1846  /* Log this relation only if needed for logical decoding */
1847  if (RelationIsLogicallyLogged(rel))
1848  relids_logged = lappend_oid(relids_logged, childrelid);
1849  }
1850  }
1851  else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1852  ereport(ERROR,
1853  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
1854  errmsg("cannot truncate only a partitioned table"),
1855  errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1856  }
1857 
1858  ExecuteTruncateGuts(rels, relids, relids_logged,
1859  stmt->behavior, stmt->restart_seqs, false);
1860 
1861  /* And close the rels */
1862  foreach(cell, rels)
1863  {
1864  Relation rel = (Relation) lfirst(cell);
1865 
1866  table_close(rel, NoLock);
1867  }
1868 }
#define RelationIsLogicallyLogged(relation)
Definition: rel.h:701
static void truncate_check_activity(Relation rel)
Definition: tablecmds.c:2337
static void truncate_check_rel(Oid relid, Form_pg_class reltuple)
Definition: tablecmds.c:2271
static void RangeVarCallbackForTruncate(const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
Definition: tablecmds.c:17621
void ExecuteTruncateGuts(List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
Definition: tablecmds.c:1884

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

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

6741 {
6742  Relation depRel;
6743  ScanKeyData key[2];
6744  SysScanDesc depScan;
6745  HeapTuple depTup;
6746 
6747  /* since this function recurses, it could be driven to stack overflow */
6749 
6750  /*
6751  * We scan pg_depend to find those things that depend on the given type.
6752  * (We assume we can ignore refobjsubid for a type.)
6753  */
6754  depRel = table_open(DependRelationId, AccessShareLock);
6755 
6756  ScanKeyInit(&key[0],
6757  Anum_pg_depend_refclassid,
6758  BTEqualStrategyNumber, F_OIDEQ,
6759  ObjectIdGetDatum(TypeRelationId));
6760  ScanKeyInit(&key[1],
6761  Anum_pg_depend_refobjid,
6762  BTEqualStrategyNumber, F_OIDEQ,
6763  ObjectIdGetDatum(typeOid));
6764 
6765  depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
6766  NULL, 2, key);
6767 
6768  while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
6769  {
6770  Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup);
6771  Relation rel;
6772  TupleDesc tupleDesc;
6773  Form_pg_attribute att;
6774 
6775  /* Check for directly dependent types */
6776  if (pg_depend->classid == TypeRelationId)
6777  {
6778  /*
6779  * This must be an array, domain, or range containing the given
6780  * type, so recursively check for uses of this type. Note that
6781  * any error message will mention the original type not the
6782  * container; this is intentional.
6783  */
6784  find_composite_type_dependencies(pg_depend->objid,
6785  origRelation, origTypeName);
6786  continue;
6787  }
6788 
6789  /* Else, ignore dependees that aren't relations */
6790  if (pg_depend->classid != RelationRelationId)
6791  continue;
6792 
6793  rel = relation_open(pg_depend->objid, AccessShareLock);
6794  tupleDesc = RelationGetDescr(rel);
6795 
6796  /*
6797  * If objsubid identifies a specific column, refer to that in error
6798  * messages. Otherwise, search to see if there's a user column of the
6799  * type. (We assume system columns are never of interesting types.)
6800  * The search is needed because an index containing an expression
6801  * column of the target type will just be recorded as a whole-relation
6802  * dependency. If we do not find a column of the type, the dependency
6803  * must indicate that the type is transiently referenced in an index
6804  * expression but not stored on disk, which we assume is OK, just as
6805  * we do for references in views. (It could also be that the target
6806  * type is embedded in some container type that is stored in an index
6807  * column, but the previous recursion should catch such cases.)
6808  */
6809  if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
6810  att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
6811  else
6812  {
6813  att = NULL;
6814  for (int attno = 1; attno <= tupleDesc->natts; attno++)
6815  {
6816  att = TupleDescAttr(tupleDesc, attno - 1);
6817  if (att->atttypid == typeOid && !att->attisdropped)
6818  break;
6819  att = NULL;
6820  }
6821  if (att == NULL)
6822  {
6823  /* No such column, so assume OK */
6825  continue;
6826  }
6827  }
6828 
6829  /*
6830  * We definitely should reject if the relation has storage. If it's
6831  * partitioned, then perhaps we don't have to reject: if there are
6832  * partitions then we'll fail when we find one, else there is no
6833  * stored data to worry about. However, it's possible that the type
6834  * change would affect conclusions about whether the type is sortable
6835  * or hashable and thus (if it's a partitioning column) break the
6836  * partitioning rule. For now, reject for partitioned rels too.
6837  */
6838  if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
6839  RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
6840  {
6841  if (origTypeName)
6842  ereport(ERROR,
6843  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6844  errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6845  origTypeName,
6847  NameStr(att->attname))));
6848  else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
6849  ereport(ERROR,
6850  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6851  errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
6852  RelationGetRelationName(origRelation),
6854  NameStr(att->attname))));
6855  else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
6856  ereport(ERROR,
6857  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6858  errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
6859  RelationGetRelationName(origRelation),
6861  NameStr(att->attname))));
6862  else
6863  ereport(ERROR,
6864  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
6865  errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
6866  RelationGetRelationName(origRelation),
6868  NameStr(att->attname))));
6869  }
6870  else if (OidIsValid(rel->rd_rel->reltype))
6871  {
6872  /*
6873  * A view or composite type itself isn't a problem, but we must
6874  * recursively check for indirect dependencies via its rowtype.
6875  */
6877  origRelation, origTypeName);
6878  }
6879 
6881  }
6882 
6883  systable_endscan(depScan);
6884 
6886 }

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

6898 {
6899  Relation classRel;
6900  ScanKeyData key[1];
6901  TableScanDesc scan;
6902  HeapTuple tuple;
6903  List *result = NIL;
6904 
6905  classRel = table_open(RelationRelationId, AccessShareLock);
6906 
6907  ScanKeyInit(&key[0],
6908  Anum_pg_class_reloftype,
6909  BTEqualStrategyNumber, F_OIDEQ,
6910  ObjectIdGetDatum(typeOid));
6911 
6912  scan = table_beginscan_catalog(classRel, 1, key);
6913 
6914  while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
6915  {
6916  Form_pg_class classform = (Form_pg_class) GETSTRUCT(tuple);
6917 
6918  if (behavior == DROP_RESTRICT)
6919  ereport(ERROR,
6920  (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
6921  errmsg("cannot alter type \"%s\" because it is the type of a typed table",
6922  typeName),
6923  errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
6924  else
6925  result = lappend_oid(result, classform->oid);
6926  }
6927 
6928  table_endscan(scan);
6929  table_close(classRel, AccessShareLock);
6930 
6931  return result;
6932 }

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

3448 {
3449  ListCell *lc;
3450  int i = 1;
3451 
3452  foreach(lc, columns)
3453  {
3454  if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3455  return i;
3456 
3457  i++;
3458  }
3459  return 0;
3460 }

References i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 12055 of file tablecmds.c.

12056 {
12057  CoercionPathType ret;
12058 
12059  if (targetTypeId == sourceTypeId)
12060  {
12062  *funcid = InvalidOid;
12063  }
12064  else
12065  {
12066  ret = find_coercion_pathway(targetTypeId, sourceTypeId,
12067  COERCION_IMPLICIT, funcid);
12068  if (ret == COERCION_PATH_NONE)
12069  /* A previously-relied-upon cast is now gone. */
12070  elog(ERROR, "could not find cast from %u to %u",
12071  sourceTypeId, targetTypeId);
12072  }
12073 
12074  return ret;
12075 }
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 19990 of file tablecmds.c.

19991 {
19992  char cmethod;
19993 
19994  if (compression == NULL || strcmp(compression, "default") == 0)
19995  return InvalidCompressionMethod;
19996 
19997  /*
19998  * To specify a nondefault method, the column data type must be toastable.
19999  * Note this says nothing about whether the column's attstorage setting
20000  * permits compression; we intentionally allow attstorage and
20001  * attcompression to be independent. But with a non-toastable type,
20002  * attstorage could not be set to a value that would permit compression.
20003  *
20004  * We don't actually need to enforce this, since nothing bad would happen
20005  * if attcompression were non-default; it would never be consulted. But
20006  * it seems more user-friendly to complain about a certainly-useless
20007  * attempt to set the property.
20008  */
20009  if (!TypeIsToastable(atttypid))
20010  ereport(ERROR,
20011  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20012  errmsg("column data type %s does not support compression",
20013  format_type_be(atttypid))));
20014 
20015  cmethod = CompressionNameToMethod(compression);
20016  if (!CompressionMethodIsValid(cmethod))
20017  ereport(ERROR,
20018  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20019  errmsg("invalid compression method \"%s\"", compression)));
20020 
20021  return cmethod;
20022 }
#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 20028 of file tablecmds.c.

20029 {
20030  char cstorage = 0;
20031 
20032  if (pg_strcasecmp(storagemode, "plain") == 0)
20033  cstorage = TYPSTORAGE_PLAIN;
20034  else if (pg_strcasecmp(storagemode, "external") == 0)
20035  cstorage = TYPSTORAGE_EXTERNAL;
20036  else if (pg_strcasecmp(storagemode, "extended") == 0)
20037  cstorage = TYPSTORAGE_EXTENDED;
20038  else if (pg_strcasecmp(storagemode, "main") == 0)
20039  cstorage = TYPSTORAGE_MAIN;
20040  else if (pg_strcasecmp(storagemode, "default") == 0)
20041  cstorage = get_typstorage(atttypid);
20042  else
20043  ereport(ERROR,
20044  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
20045  errmsg("invalid storage type \"%s\"",
20046  storagemode)));
20047 
20048  /*
20049  * safety check: do not allow toasted storage modes unless column datatype
20050  * is TOAST-aware.
20051  */
20052  if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
20053  ereport(ERROR,
20054  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
20055  errmsg("column data type %s can only have storage PLAIN",
20056  format_type_be(atttypid))));
20057 
20058  return cstorage;
20059 }
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 11160 of file tablecmds.c.

11164 {
11165  ScanKeyData key;
11166  SysScanDesc scan;
11167  HeapTuple trigtup;
11168 
11169  *deleteTriggerOid = *updateTriggerOid = InvalidOid;
11170  ScanKeyInit(&key,
11171  Anum_pg_trigger_tgconstraint,
11172  BTEqualStrategyNumber, F_OIDEQ,
11173  ObjectIdGetDatum(conoid));
11174 
11175  scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11176  NULL, 1, &key);
11177  while ((trigtup = systable_getnext(scan)) != NULL)
11178  {
11179  Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11180 
11181  if (trgform->tgconstrrelid != conrelid)
11182  continue;
11183  if (trgform->tgrelid != confrelid)
11184  continue;
11185  /* Only ever look at "action" triggers on the PK side. */
11186  if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_PK)
11187  continue;
11188  if (TRIGGER_FOR_DELETE(trgform->tgtype))
11189  {
11190  Assert(*deleteTriggerOid == InvalidOid);
11191  *deleteTriggerOid = trgform->oid;
11192  }
11193  else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11194  {
11195  Assert(*updateTriggerOid == InvalidOid);
11196  *updateTriggerOid = trgform->oid;
11197  }
11198 #ifndef USE_ASSERT_CHECKING
11199  /* In an assert-enabled build, continue looking to find duplicates */
11200  if (OidIsValid(*deleteTriggerOid) && OidIsValid(*updateTriggerOid))
11201  break;
11202 #endif
11203  }
11204 
11205  if (!OidIsValid(*deleteTriggerOid))
11206  elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
11207  conoid);
11208  if (!OidIsValid(*updateTriggerOid))
11209  elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
11210  conoid);
11211 
11212  systable_endscan(scan);
11213 }
int RI_FKey_trigger_type(Oid tgfoid)
Definition: ri_triggers.c:3001
#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 11221 of file tablecmds.c.

11225 {
11226  ScanKeyData key;
11227  SysScanDesc scan;
11228  HeapTuple trigtup;
11229 
11230  *insertTriggerOid = *updateTriggerOid = InvalidOid;
11231  ScanKeyInit(&key,
11232  Anum_pg_trigger_tgconstraint,
11233  BTEqualStrategyNumber, F_OIDEQ,
11234  ObjectIdGetDatum(conoid));
11235 
11236  scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11237  NULL, 1, &key);
11238  while ((trigtup = systable_getnext(scan)) != NULL)
11239  {
11240  Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11241 
11242  if (trgform->tgconstrrelid != confrelid)
11243  continue;
11244  if (trgform->tgrelid != conrelid)
11245  continue;
11246  /* Only ever look at "check" triggers on the FK side. */
11247  if (RI_FKey_trigger_type(trgform->tgfoid) != RI_TRIGGER_FK)
11248  continue;
11249  if (TRIGGER_FOR_INSERT(trgform->tgtype))
11250  {
11251  Assert(*insertTriggerOid == InvalidOid);
11252  *insertTriggerOid = trgform->oid;
11253  }
11254  else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
11255  {
11256  Assert(*updateTriggerOid == InvalidOid);
11257  *updateTriggerOid = trgform->oid;
11258  }
11259 #ifndef USE_ASSERT_CHECKING
11260  /* In an assert-enabled build, continue looking to find duplicates. */
11261  if (OidIsValid(*insertTriggerOid) && OidIsValid(*updateTriggerOid))
11262  break;
11263 #endif
11264  }
11265 
11266  if (!OidIsValid(*insertTriggerOid))
11267  elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
11268  conoid);
11269  if (!OidIsValid(*updateTriggerOid))
11270  elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
11271  conoid);
11272 
11273  systable_endscan(scan);
11274 }
#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 19888 of file tablecmds.c.

19889 {
19890  Relation pg_constraint;
19891  HeapTuple tuple;
19892  SysScanDesc scan;
19893  ScanKeyData key[2];
19894  List *constraints = NIL;
19895 
19896  /*
19897  * If no indexes, or no columns are referenceable by FKs, we can avoid the
19898  * scan.
19899  */
19900  if (RelationGetIndexList(partition) == NIL ||
19903  return NIL;
19904 
19905  /* Search for constraints referencing this table */
19906  pg_constraint = table_open(ConstraintRelationId, AccessShareLock);
19907  ScanKeyInit(&key[0],
19908  Anum_pg_constraint_confrelid, BTEqualStrategyNumber,
19909  F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(partition)));
19910  ScanKeyInit(&key[1],
19911  Anum_pg_constraint_contype, BTEqualStrategyNumber,
19912  F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN));
19913 
19914  /* XXX This is a seqscan, as we don't have a usable index */
19915  scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
19916  while ((tuple = systable_getnext(scan)) != NULL)
19917  {
19918  Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple);
19919 
19920  /*
19921  * We only need to process constraints that are part of larger ones.
19922  */
19923  if (!OidIsValid(constrForm->conparentid))
19924  continue;
19925 
19926  constraints = lappend_oid(constraints, constrForm->oid);
19927  }
19928 
19929  systable_endscan(scan);
19930  table_close(pg_constraint, AccessShareLock);
19931 
19932  return constraints;
19933 }
#define bms_is_empty(a)
Definition: bitmapset.h:118
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition: relcache.c:5244
@ 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 15384 of file tablecmds.c.

15385 {
15386  SMgrRelation dstrel;
15387 
15388  /*
15389  * Since we copy the file directly without looking at the shared buffers,
15390  * we'd better first flush out any pages of the source relation that are
15391  * in shared buffers. We assume no new changes will be made while we are
15392  * holding exclusive lock on the rel.
15393  */
15394  FlushRelationBuffers(rel);
15395 
15396  /*
15397  * Create and copy all forks of the relation, and schedule unlinking of
15398  * old physical files.
15399  *
15400  * NOTE: any conflict in relfilenumber value will be caught in
15401  * RelationCreateStorage().
15402  */
15403  dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
15404 
15405  /* copy main fork */
15407  rel->rd_rel->relpersistence);
15408 
15409  /* copy those extra forks that exist */
15410  for (ForkNumber forkNum = MAIN_FORKNUM + 1;
15411  forkNum <= MAX_FORKNUM; forkNum++)
15412  {
15413  if (smgrexists(RelationGetSmgr(rel), forkNum))
15414  {
15415  smgrcreate(dstrel, forkNum, false);
15416 
15417  /*
15418  * WAL log creation if the relation is persistent, or this is the
15419  * init fork of an unlogged relation.
15420  */
15421  if (RelationIsPermanent(rel) ||
15422  (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
15423  forkNum == INIT_FORKNUM))
15424  log_smgrcreate(&newrlocator, forkNum);
15425  RelationCopyStorage(RelationGetSmgr(rel), dstrel, forkNum,
15426  rel->rd_rel->relpersistence);
15427  }
15428  }
15429 
15430  /* drop old relation, and close new one */
15431  RelationDropStorage(rel);
15432  smgrclose(dstrel);
15433 }
void FlushRelationBuffers(Relation rel)
Definition: bufmgr.c:4504
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 16020 of file tablecmds.c.

16021 {
16022  Relation catalogRelation;
16023  SysScanDesc scan;
16024  ScanKeyData key;
16025  HeapTuple inheritsTuple;
16026  bool found = false;
16027 
16028  Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16029 
16030  /*
16031  * Find pg_inherits entries by inhparent. (We need to scan them all in
16032  * order to verify that no other partition is pending detach.)
16033  */
16034  catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
16035  ScanKeyInit(&key,
16036  Anum_pg_inherits_inhparent,
16037  BTEqualStrategyNumber, F_OIDEQ,
16038  ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16039  scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
16040  true, NULL, 1, &key);
16041 
16042  while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
16043  {
16044  Form_pg_inherits inhForm;
16045 
16046  inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
16047  if (inhForm->inhdetachpending)
16048  ereport(ERROR,
16049  errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
16050  errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
16051  get_rel_name(inhForm->inhrelid),
16052  get_namespace_name(parent_rel->rd_rel->relnamespace),
16053  RelationGetRelationName(parent_rel)),
16054  errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
16055 
16056  if (inhForm->inhrelid == RelationGetRelid(child_rel))
16057  {
16058  HeapTuple newtup;
16059 
16060  newtup = heap_copytuple(inheritsTuple);
16061  ((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
16062 
16063  CatalogTupleUpdate(catalogRelation,
16064  &inheritsTuple->t_self,
16065  newtup);
16066  found = true;
16067  heap_freetuple(newtup);
16068  /* keep looking, to ensure we catch others pending detach */
16069  }
16070  }
16071 
16072  /* Done */
16073  systable_endscan(scan);
16074  table_close(catalogRelation, RowExclusiveLock);
16075 
16076  if (!found)
16077  ereport(ERROR,
16079  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16080  RelationGetRelationName(child_rel),
16081  RelationGetRelationName(parent_rel))));
16082 }
#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 
)
static

Definition at line 2439 of file tablecmds.c.

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

References aclcheck_error(), ACLCHECK_NOT_OWNER, Assert, AttrMap::attnums, ConstrCheck::ccname, TupleConstr::check, CheckTableNotInUse(), ColumnDef::colname, ColumnDef::compression, CompressionMethodIsValid, TupleDescData::constr, ColumnDef::constraints, ColumnDef::cooked_default, elog, equal(), ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, findAttrByName(), forboth, free_attrmap(), ColumnDef::generated, get_relkind_objtype(), GetCompressionMethodName(), GetUserId(), i, ColumnDef::identity, ColumnDef::inhcount, InvalidOid, ColumnDef::is_from_type, ColumnDef::is_local, ColumnDef::is_not_null, lappend(), lfirst, lfirst_node, lfirst_oid, list_delete_nth_cell(), list_length(), list_nth_node, make_attrmap(), makeColumnDef(), map_variable_attnos(), MaxHeapAttributeNumber, MergeCheckConstraint(), MergeChildAttribute(), MergeInheritedAttribute(), name, NameStr, TupleDescData::natts, NIL, NoLock, TupleConstr::num_check, object_ownercheck(), pstrdup(), ColumnDef::raw_default, RelationData::rd_islocaltemp, RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ColumnDef::storage, stringToNode(), table_close(), table_open(), TupleDescAttr, TupleDescGetDefault(), and ColumnDef::typeName.

Referenced by DefineRelation().

◆ MergeAttributesIntoExisting()

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

Definition at line 15733 of file tablecmds.c.

15734 {
15735  Relation attrrel;
15736  TupleDesc parent_desc;
15737 
15738  attrrel = table_open(AttributeRelationId, RowExclusiveLock);
15739  parent_desc = RelationGetDescr(parent_rel);
15740 
15741  for (AttrNumber parent_attno = 1; parent_attno <= parent_desc->natts; parent_attno++)
15742  {
15743  Form_pg_attribute parent_att = TupleDescAttr(parent_desc, parent_attno - 1);
15744  char *parent_attname = NameStr(parent_att->attname);
15745  HeapTuple tuple;
15746 
15747  /* Ignore dropped columns in the parent. */
15748  if (parent_att->attisdropped)
15749  continue;
15750 
15751  /* Find same column in child (matching on column name). */
15752  tuple = SearchSysCacheCopyAttName(RelationGetRelid(child_rel), parent_attname);
15753  if (HeapTupleIsValid(tuple))
15754  {
15755  Form_pg_attribute child_att = (Form_pg_attribute) GETSTRUCT(tuple);
15756 
15757  if (parent_att->atttypid != child_att->atttypid ||
15758  parent_att->atttypmod != child_att->atttypmod)
15759  ereport(ERROR,
15760  (errcode(ERRCODE_DATATYPE_MISMATCH),
15761  errmsg("child table \"%s\" has different type for column \"%s\"",
15762  RelationGetRelationName(child_rel), parent_attname)));
15763 
15764  if (parent_att->attcollation != child_att->attcollation)
15765  ereport(ERROR,
15766  (errcode(ERRCODE_COLLATION_MISMATCH),
15767  errmsg("child table \"%s\" has different collation for column \"%s\"",
15768  RelationGetRelationName(child_rel), parent_attname)));
15769 
15770  /*
15771  * Check child doesn't discard NOT NULL property. (Other
15772  * constraints are checked elsewhere.)
15773  */
15774  if (parent_att->attnotnull && !child_att->attnotnull)
15775  ereport(ERROR,
15776  (errcode(ERRCODE_DATATYPE_MISMATCH),
15777  errmsg("column \"%s\" in child table must be marked NOT NULL",
15778  parent_attname)));
15779 
15780  /*
15781  * Child column must be generated if and only if parent column is.
15782  */
15783  if (parent_att->attgenerated && !child_att->attgenerated)
15784  ereport(ERROR,
15785  (errcode(ERRCODE_DATATYPE_MISMATCH),
15786  errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
15787  if (child_att->attgenerated && !parent_att->attgenerated)
15788  ereport(ERROR,
15789  (errcode(ERRCODE_DATATYPE_MISMATCH),
15790  errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
15791 
15792  /*
15793  * Regular inheritance children are independent enough not to
15794  * inherit identity columns. But partitions are integral part of
15795  * a partitioned table and inherit identity column.
15796  */
15797  if (ispartition)
15798  child_att->attidentity = parent_att->attidentity;
15799 
15800  /*
15801  * OK, bump the child column's inheritance count. (If we fail
15802  * later on, this change will just roll back.)
15803  */
15804  child_att->attinhcount++;
15805  if (child_att->attinhcount < 0)
15806  ereport(ERROR,
15807  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15808  errmsg("too many inheritance parents"));
15809 
15810  /*
15811  * In case of partitions, we must enforce that value of attislocal
15812  * is same in all partitions. (Note: there are only inherited
15813  * attributes in partitions)
15814  */
15815  if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15816  {
15817  Assert(child_att->attinhcount == 1);
15818  child_att->attislocal = false;
15819  }
15820 
15821  CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
15822  heap_freetuple(tuple);
15823  }
15824  else
15825  {
15826  ereport(ERROR,
15827  (errcode(ERRCODE_DATATYPE_MISMATCH),
15828  errmsg("child table is missing column \"%s\"", parent_attname)));
15829  }
15830  }
15831 
15832  table_close(attrrel, RowExclusiveLock);
15833 }

References Assert, CatalogTupleUpdate(), ereport, errcode(), errmsg(), ERROR, 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 3021 of file tablecmds.c.

3022 {
3023  ListCell *lc;
3024  CookedConstraint *newcon;
3025 
3026  foreach(lc, constraints)
3027  {
3028  CookedConstraint *ccon = (CookedConstraint *) lfirst(lc);
3029 
3030  Assert(ccon->contype == CONSTR_CHECK);
3031 
3032  /* Non-matching names never conflict */
3033  if (strcmp(ccon->name, name) != 0)
3034  continue;
3035 
3036  if (equal(expr, ccon->expr))
3037  {
3038  /* OK to merge constraint with existing */
3039  ccon->inhcount++;
3040  if (ccon->inhcount < 0)
3041  ereport(ERROR,
3042  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3043  errmsg("too many inheritance parents"));
3044  return constraints;
3045  }
3046 
3047  ereport(ERROR,
3049  errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3050  name)));
3051  }
3052 
3053  /*
3054  * Constraint couldn't be merged with an existing one and also didn't
3055  * conflict with an existing one, so add it as a new one to the list.
3056  */
3057  newcon = palloc0_object(CookedConstraint);
3058  newcon->contype = CONSTR_CHECK;
3059  newcon->name = pstrdup(name);
3060  newcon->expr = expr;
3061  newcon->inhcount = 1;
3062  return lappend(constraints, newcon);
3063 }
#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 3087 of file tablecmds.c.

3088 {
3089  char *attributeName = newdef->colname;
3090  ColumnDef *inhdef;
3091  Oid inhtypeid,
3092  newtypeid;
3093  int32 inhtypmod,
3094  newtypmod;
3095  Oid inhcollid,
3096  newcollid;
3097 
3098  if (exist_attno == newcol_attno)
3099  ereport(NOTICE,
3100  (errmsg("merging column \"%s\" with inherited definition",
3101  attributeName)));
3102  else
3103  ereport(NOTICE,
3104  (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3105  errdetail("User-specified column moved to the position of the inherited column.")));
3106 
3107  inhdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3108 
3109  /*
3110  * Must have the same type and typmod
3111  */
3112  typenameTypeIdAndMod(NULL, inhdef->typeName, &inhtypeid, &inhtypmod);
3113  typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3114  if (inhtypeid != newtypeid || inhtypmod != newtypmod)
3115  ereport(ERROR,
3116  (errcode(ERRCODE_DATATYPE_MISMATCH),
3117  errmsg("column \"%s\" has a type conflict",
3118  attributeName),
3119  errdetail("%s versus %s",
3120  format_type_with_typemod(inhtypeid, inhtypmod),
3121  format_type_with_typemod(newtypeid, newtypmod))));
3122 
3123  /*
3124  * Must have the same collation
3125  */
3126  inhcollid = GetColumnDefCollation(NULL, inhdef, inhtypeid);
3127  newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3128  if (inhcollid != newcollid)
3129  ereport(ERROR,
3130  (errcode(ERRCODE_COLLATION_MISMATCH),
3131  errmsg("column \"%s\" has a collation conflict",
3132  attributeName),
3133  errdetail("\"%s\" versus \"%s\"",
3134  get_collation_name(inhcollid),
3135  get_collation_name(newcollid))));
3136 
3137  /*
3138  * Identity is never inherited by a regular inheritance child. Pick
3139  * child's identity definition if there's one.
3140  */
3141  inhdef->identity = newdef->identity;
3142 
3143  /*
3144  * Copy storage parameter
3145  */
3146  if (inhdef->storage == 0)
3147  inhdef->storage = newdef->storage;
3148  else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3149  ereport(ERROR,
3150  (errcode(ERRCODE_DATATYPE_MISMATCH),
3151  errmsg("column \"%s\" has a storage parameter conflict",
3152  attributeName),
3153  errdetail("%s versus %s",
3154  storage_name(inhdef->storage),
3155  storage_name(newdef->storage))));
3156 
3157  /*
3158  * Copy compression parameter
3159  */
3160  if (inhdef->compression == NULL)
3161  inhdef->compression = newdef->compression;
3162  else if (newdef->compression != NULL)
3163  {
3164  if (strcmp(inhdef->compression, newdef->compression) != 0)
3165  ereport(ERROR,
3166  (errcode(ERRCODE_DATATYPE_MISMATCH),
3167  errmsg("column \"%s\" has a compression method conflict",
3168  attributeName),
3169  errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3170  }
3171 
3172  /*
3173  * Merge of not-null constraints = OR 'em together
3174  */
3175  inhdef->is_not_null |= newdef->is_not_null;
3176 
3177  /*
3178  * Check for conflicts related to generated columns.
3179  *
3180  * If the parent column is generated, the child column will be made a
3181  * generated column if it isn't already. If it is a generated column,
3182  * we'll take its generation expression in preference to the parent's. We
3183  * must check that the child column doesn't specify a default value or
3184  * identity, which matches the rules for a single column in
3185  * parse_utilcmd.c.
3186  *
3187  * Conversely, if the parent column is not generated, the child column
3188  * can't be either. (We used to allow that, but it results in being able
3189  * to override the generation expression via UPDATEs through the parent.)
3190  */
3191  if (inhdef->generated)
3192  {
3193  if (newdef->raw_default && !newdef->generated)
3194  ereport(ERROR,
3195  (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3196  errmsg("column \"%s\" inherits from generated column but specifies default",
3197  inhdef->colname)));
3198  if (newdef->identity)
3199  ereport(ERROR,
3200  (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3201  errmsg("column \"%s\" inherits from generated column but specifies identity",
3202  inhdef->colname)));
3203  }
3204  else
3205  {
3206  if (newdef->generated)
3207  ereport(ERROR,
3208  (errcode(ERRCODE_INVALID_COLUMN_DEFINITION),
3209  errmsg("child column \"%s\" specifies generation expression",
3210  inhdef->colname),
3211  errhint("A child table column cannot be generated unless its parent column is.")));
3212  }
3213 
3214  /*
3215  * If new def has a default, override previous default
3216  */
3217  if (newdef->raw_default != NULL)
3218  {
3219  inhdef->raw_default = newdef->raw_default;
3220  inhdef->cooked_default = newdef->cooked_default;
3221  }
3222 
3223  /* Mark the column as locally defined */
3224  inhdef->is_local = true;
3225 }
static const char * storage_name(char c)
Definition: tablecmds.c:2360

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

15854 {
15855  Relation constraintrel;
15856  SysScanDesc parent_scan;
15857  ScanKeyData parent_key;
15858  HeapTuple parent_tuple;
15859  Oid parent_relid = RelationGetRelid(parent_rel);
15860 
15861  constraintrel = table_open(ConstraintRelationId, RowExclusiveLock);
15862 
15863  /* Outer loop scans through the parent's constraint definitions */
15864  ScanKeyInit(&parent_key,
15865  Anum_pg_constraint_conrelid,
15866  BTEqualStrategyNumber, F_OIDEQ,
15867  ObjectIdGetDatum(parent_relid));
15868  parent_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
15869  true, NULL, 1, &parent_key);
15870 
15871  while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
15872  {
15873  Form_pg_constraint parent_con = (Form_pg_constraint) GETSTRUCT(parent_tuple);
15874  SysScanDesc child_scan;
15875  ScanKeyData child_key;
15876  HeapTuple child_tuple;
15877  bool found = false;
15878 
15879  if (parent_con->contype != CONSTRAINT_CHECK)
15880  continue;
15881 
15882  /* if the parent's constraint is marked NO INHERIT, it's not inherited */
15883  if (parent_con->connoinherit)
15884  continue;
15885 
15886  /* Search for a child constraint matching this one */
15887  ScanKeyInit(&child_key,
15888  Anum_pg_constraint_conrelid,
15889  BTEqualStrategyNumber, F_OIDEQ,
15890  ObjectIdGetDatum(RelationGetRelid(child_rel)));
15891  child_scan = systable_beginscan(constraintrel, ConstraintRelidTypidNameIndexId,
15892  true, NULL, 1, &child_key);
15893 
15894  while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
15895  {
15896  Form_pg_constraint child_con = (Form_pg_constraint) GETSTRUCT(child_tuple);
15897  HeapTuple child_copy;
15898 
15899  if (child_con->contype != CONSTRAINT_CHECK)
15900  continue;
15901 
15902  if (strcmp(NameStr(parent_con->conname),
15903  NameStr(child_con->conname)) != 0)
15904  continue;
15905 
15906  if (!constraints_equivalent(parent_tuple, child_tuple, RelationGetDescr(constraintrel)))
15907  ereport(ERROR,
15908  (errcode(ERRCODE_DATATYPE_MISMATCH),
15909  errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
15910  RelationGetRelationName(child_rel), NameStr(parent_con->conname))));
15911 
15912  /* If the child constraint is "no inherit" then cannot merge */
15913  if (child_con->connoinherit)
15914  ereport(ERROR,
15915  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
15916  errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
15917  NameStr(child_con->conname), RelationGetRelationName(child_rel))));
15918 
15919  /*
15920  * If the child constraint is "not valid" then cannot merge with a
15921  * valid parent constraint
15922  */
15923  if (parent_con->convalidated && !child_con->convalidated)
15924  ereport(ERROR,
15925  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
15926  errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
15927  NameStr(child_con->conname), RelationGetRelationName(child_rel))));
15928 
15929  /*
15930  * OK, bump the child constraint's inheritance count. (If we fail
15931  * later on, this change will just roll back.)
15932  */
15933  child_copy = heap_copytuple(child_tuple);
15934  child_con = (Form_pg_constraint) GETSTRUCT(child_copy);
15935  child_con->coninhcount++;
15936  if (child_con->coninhcount < 0)
15937  ereport(ERROR,
15938  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
15939  errmsg("too many inheritance parents"));
15940 
15941  /*
15942  * In case of partitions, an inherited constraint must be
15943  * inherited only once since it cannot have multiple parents and
15944  * it is never considered local.
15945  */
15946  if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
15947  {
15948  Assert(child_con->coninhcount == 1);
15949  child_con->conislocal = false;
15950  }
15951 
15952  CatalogTupleUpdate(constraintrel, &child_copy->t_self, child_copy);
15953  heap_freetuple(child_copy);
15954 
15955  found = true;
15956  break;
15957  }
15958 
15959  systable_endscan(child_scan);
15960 
15961  if (!found)
15962  ereport(ERROR,
15963  (errcode(ERRCODE_DATATYPE_MISMATCH),
15964  errmsg("child table is missing constraint \"%s\"",
15965  NameStr(parent_con->conname))));
15966  }
15967 
15968  systable_endscan(parent_scan);
15969  table_close(constraintrel, RowExclusiveLock);
15970 }
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
Definition: tablecmds.c:15704

References Assert, BTEqualStrategyNumber, CatalogTupleUpdate(), constraints_equivalent(), ereport, errcode(), errmsg(), ERROR, 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 3248 of file tablecmds.c.

3251 {
3252  char *attributeName = newdef->colname;
3253  ColumnDef *prevdef;
3254  Oid prevtypeid,
3255  newtypeid;
3256  int32 prevtypmod,
3257  newtypmod;
3258  Oid prevcollid,
3259  newcollid;
3260 
3261  ereport(NOTICE,
3262  (errmsg("merging multiple inherited definitions of column \"%s\"",
3263  attributeName)));
3264  prevdef = list_nth_node(ColumnDef, inh_columns, exist_attno - 1);
3265 
3266  /*
3267  * Must have the same type and typmod
3268  */
3269  typenameTypeIdAndMod(NULL, prevdef->typeName, &prevtypeid, &prevtypmod);
3270  typenameTypeIdAndMod(NULL, newdef->typeName, &newtypeid, &newtypmod);
3271  if (prevtypeid != newtypeid || prevtypmod != newtypmod)
3272  ereport(ERROR,
3273  (errcode(ERRCODE_DATATYPE_MISMATCH),
3274  errmsg("inherited column \"%s\" has a type conflict",
3275  attributeName),
3276  errdetail("%s versus %s",
3277  format_type_with_typemod(prevtypeid, prevtypmod),
3278  format_type_with_typemod(newtypeid, newtypmod))));
3279 
3280  /*
3281  * Merge of not-null constraints = OR 'em together
3282  */
3283  prevdef->is_not_null |= newdef->is_not_null;
3284 
3285  /*
3286  * Must have the same collation
3287  */
3288  prevcollid = GetColumnDefCollation(NULL, prevdef, prevtypeid);
3289  newcollid = GetColumnDefCollation(NULL, newdef, newtypeid);
3290  if (prevcollid != newcollid)
3291  ereport(ERROR,
3292  (errcode(ERRCODE_COLLATION_MISMATCH),
3293  errmsg("inherited column \"%s\" has a collation conflict",
3294  attributeName),
3295  errdetail("\"%s\" versus \"%s\"",
3296  get_collation_name(prevcollid),
3297  get_collation_name(newcollid))));
3298 
3299  /*
3300  * Copy/check storage parameter
3301  */
3302  if (prevdef->storage == 0)
3303  prevdef->storage = newdef->storage;
3304  else if (prevdef->storage != newdef->storage)
3305  ereport(ERROR,
3306  (errcode(ERRCODE_DATATYPE_MISMATCH),
3307  errmsg("inherited column \"%s\" has a storage parameter conflict",
3308  attributeName),
3309  errdetail("%s versus %s",
3310  storage_name(prevdef->storage),
3311  storage_name(newdef->storage))));
3312 
3313  /*
3314  * Copy/check compression parameter
3315  */
3316  if (prevdef->compression == NULL)
3317  prevdef->compression = newdef->compression;
3318  else if (newdef->compression != NULL)
3319  {
3320  if (strcmp(prevdef->compression, newdef->compression) != 0)
3321  ereport(ERROR,
3322  (errcode(ERRCODE_DATATYPE_MISMATCH),
3323  errmsg("column \"%s\" has a compression method conflict",
3324  attributeName),
3325  errdetail("%s versus %s",
3326  prevdef->compression, newdef->compression)));
3327  }
3328 
3329  /*
3330  * Check for GENERATED conflicts
3331  */
3332  if (prevdef->generated != newdef->generated)
3333  ereport(ERROR,
3334  (errcode(ERRCODE_DATATYPE_MISMATCH),
3335  errmsg("inherited column \"%s\" has a generation conflict",
3336  attributeName)));
3337 
3338  /*
3339  * Default and other constraints are handled by the caller.
3340  */
3341 
3342  prevdef->inhcount++;
3343  if (prevdef->inhcount < 0)
3344  ereport(ERROR,
3345  errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
3346  errmsg("too many inheritance parents"));
3347 
3348  return prevdef;
3349 }

References ColumnDef::colname, ColumnDef::compression, ereport, errcode(), errdetail(), errmsg(), ERROR, format_type_with_typemod(), ColumnDef::generated, get_collation_name(), GetColumnDefCollation(), ColumnDef::inhcount, ColumnDef::is_not_null, 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 20547 of file tablecmds.c.

20549 {
20550  CommandId mycid;
20551 
20552  /* The FSM is empty, so don't bother using it. */
20553  int ti_options = TABLE_INSERT_SKIP_FSM;
20554  ListCell *listptr;
20555  BulkInsertState bistate; /* state of bulk inserts for partition */
20556  TupleTableSlot *dstslot;
20557 
20558  mycid = GetCurrentCommandId(true);
20559 
20560  /* Prepare a BulkInsertState for table_tuple_insert. */
20561  bistate = GetBulkInsertState();
20562 
20563  /* Create necessary tuple slot. */
20564  dstslot = MakeSingleTupleTableSlot(RelationGetDescr(newPartRel),
20565  table_slot_callbacks(newPartRel));
20566  ExecStoreAllNullTuple(dstslot);
20567 
20568  foreach(listptr, mergingPartitionsList)
20569  {
20570  Relation mergingPartition = (Relation) lfirst(listptr);
20571  TupleTableSlot *srcslot;
20572  TupleConversionMap *tuple_map;
20573  TableScanDesc scan;
20574  Snapshot snapshot;
20575 
20576  /* Create tuple slot for new partition. */
20577  srcslot = MakeSingleTupleTableSlot(RelationGetDescr(mergingPartition),
20578  table_slot_callbacks(mergingPartition));
20579 
20580  /*
20581  * Map computing for moving attributes of merged partition to new
20582  * partition.
20583  */
20584  tuple_map = convert_tuples_by_name(RelationGetDescr(mergingPartition),
20585  RelationGetDescr(newPartRel));
20586 
20587  /* Scan through the rows. */
20588  snapshot = RegisterSnapshot(GetLatestSnapshot());
20589  scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
20590 
20591  while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
20592  {
20593  TupleTableSlot *insertslot;
20594 
20595  /* Extract data from old tuple. */
20596  slot_getallattrs(srcslot);
20597 
20598  if (tuple_map)
20599  {
20600  /* Need to use map to copy attributes. */
20601  insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
20602  }
20603  else
20604  {
20605  /* Copy attributes directly. */
20606  insertslot = dstslot;
20607 
20608  ExecClearTuple(insertslot);
20609 
20610  memcpy(insertslot->tts_values, srcslot->tts_values,
20611  sizeof(Datum) * srcslot->tts_nvalid);
20612  memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
20613  sizeof(bool) * srcslot->tts_nvalid);
20614 
20615  ExecStoreVirtualTuple(insertslot);
20616  }
20617 
20618  /* Write the tuple out to the new relation. */
20619  table_tuple_insert(newPartRel, insertslot, mycid,
20620  ti_options, bistate);
20621 
20623  }
20624 
20625  table_endscan(scan);
20626  UnregisterSnapshot(snapshot);
20627 
20628  if (tuple_map)
20629  free_conversion_map(tuple_map);
20630 
20632  }
20633 
20635  FreeBulkInsertState(bistate);
20636 
20637  table_finish_bulk_insert(newPartRel, ti_options);
20638 }
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 20123 of file tablecmds.c.

20124 {
20125  /* The FSM is empty, so don't bother using it. */
20126  int ti_options = TABLE_INSERT_SKIP_FSM;
20127  CommandId mycid;
20128  EState *estate;
20129  ListCell *listptr,
20130  *listptr2;
20131  TupleTableSlot *srcslot;
20132  ExprContext *econtext;
20133  TableScanDesc scan;
20134  Snapshot snapshot;
20135  MemoryContext oldCxt;
20136  List *partContexts = NIL;
20137  TupleConversionMap *tuple_map;
20138  SplitPartitionContext *defaultPartCtx = NULL,
20139  *pc;
20140  bool isOldDefaultPart = false;
20141 
20142  mycid = GetCurrentCommandId(true);
20143 
20144  estate = CreateExecutorState();
20145 
20146  forboth(listptr, partlist, listptr2, newPartRels)
20147  {
20148  SinglePartitionSpec *sps = (SinglePartitionSpec *) lfirst(listptr);
20149 
20150  pc = createSplitPartitionContext((Relation) lfirst(listptr2));
20151 
20152  if (sps->bound->is_default)
20153  {
20154  /* We should not create constraint for detached DEFAULT partition. */
20155  defaultPartCtx = pc;
20156  }
20157  else
20158  {
20159  List *partConstraint;
20160 
20161  /* Build expression execution states for partition check quals. */
20162  partConstraint = get_qual_from_partbound(rel, sps->bound);
20163  partConstraint =
20164  (List *) eval_const_expressions(NULL,
20165  (Node *) partConstraint);
20166  /* Make boolean expression for ExecCheck(). */
20167  partConstraint = list_make1(make_ands_explicit(partConstraint));
20168 
20169  /*
20170  * Map the vars in the constraint expression from rel's attnos to
20171  * splitRel's.
20172  */
20173  partConstraint = map_partition_varattnos(partConstraint,
20174  1, splitRel, rel);
20175 
20176  pc->partqualstate =
20177  ExecPrepareExpr((Expr *) linitial(partConstraint), estate);
20178  Assert(pc->partqualstate != NULL);
20179  }
20180 
20181  /* Store partition context into list. */
20182  partContexts = lappend(partContexts, pc);
20183  }
20184 
20185  /*
20186  * Create partition context for DEFAULT partition. We can insert values
20187  * into this partition in case spaces with values between new partitions.
20188  */
20189  if (!defaultPartCtx && OidIsValid(defaultPartOid))
20190  {
20191  /* Indicate that we allocate context for old DEFAULT partition */
20192  isOldDefaultPart = true;
20193  defaultPartCtx = createSplitPartitionContext(table_open(defaultPartOid, AccessExclusiveLock));
20194  }
20195 
20196  econtext = GetPerTupleExprContext(estate);
20197 
20198  /* Create necessary tuple slot. */
20199  srcslot = MakeSingleTupleTableSlot(RelationGetDescr(splitRel),
20200  table_slot_callbacks(splitRel));
20201 
20202  /*
20203  * Map computing for moving attributes of split partition to new partition
20204  * (for first new partition, but other new partitions can use the same
20205  * map).
20206  */
20207  pc = (SplitPartitionContext *) lfirst(list_head(partContexts));
20208  tuple_map = convert_tuples_by_name(RelationGetDescr(splitRel),
20209  RelationGetDescr(pc->partRel));
20210 
20211  /* Scan through the rows. */
20212  snapshot = RegisterSnapshot(GetLatestSnapshot());
20213  scan = table_beginscan(splitRel, snapshot, 0, NULL);
20214 
20215  /*
20216  * Switch to per-tuple memory context and reset it for each tuple
20217  * produced, so we don't leak memory.
20218  */
20220 
20221  while (table_scan_getnextslot(scan, ForwardScanDirection, srcslot))
20222  {
20223  bool found = false;
20224  TupleTableSlot *insertslot;
20225 
20226  /* Extract data from old tuple. */
20227  slot_getallattrs(srcslot);
20228 
20229  econtext->ecxt_scantuple = srcslot;
20230 
20231  /* Search partition for current slot srcslot. */
20232  foreach(listptr, partContexts)
20233  {
20234  pc = (SplitPartitionContext *) lfirst(listptr);
20235 
20236  if (pc->partqualstate /* skip DEFAULT partition */ &&
20237  ExecCheck(pc->partqualstate, econtext))
20238  {
20239  found = true;
20240  break;
20241  }
20242  ResetExprContext(econtext);
20243  }
20244  if (!found)
20245  {
20246  /* Use DEFAULT partition if it exists. */
20247  if (defaultPartCtx)
20248  pc = defaultPartCtx;
20249  else
20250  ereport(ERROR,
20251  (errcode(ERRCODE_CHECK_VIOLATION),
20252  errmsg("can not find partition for split partition row"),
20253  errtable(splitRel)));
20254  }
20255 
20256  if (tuple_map)
20257  {
20258  /* Need to use map to copy attributes. */
20259  insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
20260  }
20261  else
20262  {
20263  /* Copy attributes directly. */
20264  insertslot = pc->dstslot;
20265 
20266  ExecClearTuple(insertslot);
20267 
20268  memcpy(insertslot->tts_values, srcslot->tts_values,
20269  sizeof(Datum) * srcslot->tts_nvalid);
20270  memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
20271  sizeof(bool) * srcslot->tts_nvalid);
20272 
20273  ExecStoreVirtualTuple(insertslot);
20274  }
20275 
20276  /* Write the tuple out to the new relation. */
20277  table_tuple_insert(pc->partRel, insertslot, mycid,
20278  ti_options, pc->bistate);
20279 
20280  ResetExprContext(econtext);
20281 
20283  }
20284 
20285  MemoryContextSwitchTo(oldCxt);
20286 
20287  table_endscan(scan);
20288  UnregisterSnapshot(snapshot);
20289 
20290  if (tuple_map)
20291  free_conversion_map(tuple_map);
20292 
20294 
20295  FreeExecutorState(estate);
20296 
20297  foreach(listptr, partContexts)
20298  deleteSplitPartitionContext((SplitPartitionContext *) lfirst(listptr), ti_options);
20299 
20300  /* Need to close table and free buffers for DEFAULT partition. */
20301  if (isOldDefaultPart)
20302  {
20303  Relation defaultPartRel = defaultPartCtx->partRel;
20304 
20305  deleteSplitPartitionContext(defaultPartCtx, ti_options);
20306  /* Keep the lock until commit. */
20307  table_close(defaultPartRel, NoLock);
20308  }
20309 }
static SplitPartitionContext * createSplitPartitionContext(Relation partRel)
Definition: tablecmds.c:20078
static void deleteSplitPartitionContext(SplitPartitionContext *pc, int ti_options)
Definition: tablecmds.c:20103

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

7847 {
7848  NullTest *nnulltest = makeNode(NullTest);
7849 
7850  nnulltest->arg = (Expr *) makeVar(1,
7851  attr->attnum,
7852  attr->atttypid,
7853  attr->atttypmod,
7854  attr->attcollation,
7855  0);
7856  nnulltest->nulltesttype = IS_NOT_NULL;
7857 
7858  /*
7859  * argisrow = false is correct even for a composite column, because
7860  * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
7861  * case, just IS DISTINCT FROM NULL.
7862  */
7863  nnulltest->argisrow = false;
7864  nnulltest->location = -1;
7865 
7866  if (ConstraintImpliedByRelConstraint(rel, list_make1(nnulltest), NIL))
7867  {
7868  ereport(DEBUG1,
7869  (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
7870  RelationGetRelationName(rel), NameStr(attr->attname))));
7871  return true;
7872  }
7873 
7874  return false;
7875 }
@ IS_NOT_NULL
Definition: primnodes.h:1948
NullTestType nulltesttype
Definition: primnodes.h:1955
ParseLoc location
Definition: primnodes.h:1958
Expr * arg
Definition: primnodes.h:1954
static bool ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)
Definition: tablecmds.c:18187

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

◆ PartConstraintImpliedByRelConstraint()

bool PartConstraintImpliedByRelConstraint ( Relation  scanrel,
List partConstraint 
)

Definition at line 18134 of file tablecmds.c.

18136 {
18137  List *existConstraint = NIL;
18138  TupleConstr *constr = RelationGetDescr(scanrel)->constr;
18139  int i;
18140 
18141  if (constr && constr->has_not_null)
18142  {
18143  int natts = scanrel->rd_att->natts;
18144 
18145  for (i = 1; i <= natts; i++)
18146  {
18147  Form_pg_attribute att = TupleDescAttr(scanrel->rd_att, i - 1);
18148 
18149  if (att->attnotnull && !att->attisdropped)
18150  {
18151  NullTest *ntest = makeNode(NullTest);
18152 
18153  ntest->arg = (Expr *) makeVar(1,
18154  i,
18155  att->atttypid,
18156  att->atttypmod,
18157  att->attcollation,
18158  0);
18159  ntest->nulltesttype = IS_NOT_NULL;
18160 
18161  /*
18162  * argisrow=false is correct even for a composite column,
18163  * because attnotnull does not represent a SQL-spec IS NOT
18164  * NULL test in such a case, just IS DISTINCT FROM NULL.
18165  */
18166  ntest->argisrow = false;
18167  ntest->location = -1;
18168  existConstraint = lappend(existConstraint, ntest);
18169  }
18170  }
18171  }
18172 
18173  return ConstraintImpliedByRelConstraint(scanrel, partConstraint, existConstraint);
18174 }
bool has_not_null
Definition: tupdesc.h:44

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

17412 {
17413  ListCell *l;
17414  List *oids_to_truncate = NIL;
17415  List *oids_to_drop = NIL;
17416 
17417  foreach(l, on_commits)
17418  {
17419  OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17420 
17421  /* Ignore entry if already dropped in this xact */
17423  continue;
17424 
17425  switch (oc->oncommit)
17426  {
17427  case ONCOMMIT_NOOP:
17429  /* Do nothing (there shouldn't be such entries, actually) */
17430  break;
17431  case ONCOMMIT_DELETE_ROWS:
17432 
17433  /*
17434  * If this transaction hasn't accessed any temporary
17435  * relations, we can skip truncating ON COMMIT DELETE ROWS
17436  * tables, as they must still be empty.
17437  */
17439  oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid);
17440  break;
17441  case ONCOMMIT_DROP:
17442  oids_to_drop = lappend_oid(oids_to_drop, oc->relid);
17443  break;
17444  }
17445  }
17446 
17447  /*
17448  * Truncate relations before dropping so that all dependencies between
17449  * relations are removed after they are worked on. Doing it like this
17450  * might be a waste as it is possible that a relation being truncated will
17451  * be dropped anyway due to its parent being dropped, but this makes the
17452  * code more robust because of not having to re-check that the relation
17453  * exists at truncation time.
17454  */
17455  if (oids_to_truncate != NIL)
17456  heap_truncate(oids_to_truncate);
17457 
17458  if (oids_to_drop != NIL)
17459  {
17460  ObjectAddresses *targetObjects = new_object_addresses();
17461 
17462  foreach(l, oids_to_drop)
17463  {
17464  ObjectAddress object;
17465 
17466  object.classId = RelationRelationId;
17467  object.objectId = lfirst_oid(l);
17468  object.objectSubId = 0;
17469 
17470  Assert(!object_address_present(&object, targetObjects));
17471 
17472  add_exact_object_address(&object, targetObjects);
17473  }
17474 
17475  /*
17476  * Object deletion might involve toast table access (to clean up
17477  * toasted catalog entries), so ensure we have a valid snapshot.
17478  */
17480 
17481  /*
17482  * Since this is an automatic drop, rather than one directly initiated
17483  * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
17484  */
17485  performMultipleDeletions(targetObjects, DROP_CASCADE,
17487 
17489 
17490 #ifdef USE_ASSERT_CHECKING
17491 
17492  /*
17493  * Note that table deletion will call remove_on_commit_action, so the
17494  * entry should get marked as deleted.
17495  */
17496  foreach(l, on_commits)
17497  {
17498  OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17499 
17500  if (oc->oncommit != ONCOMMIT_DROP)
17501  continue;
17502 
17504  }
17505 #endif
17506  }
17507 }
#define PERFORM_DELETION_QUIETLY
Definition: dependency.h:94
void heap_truncate(List *relids)
Definition: heap.c:3018
@ 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 18244 of file tablecmds.c.

18247 {
18248  /*
18249  * Based on the table's existing constraints, determine whether or not we
18250  * may skip scanning the table.
18251  */
18252  if (PartConstraintImpliedByRelConstraint(scanrel, partConstraint))
18253  {
18254  if (!validate_default)
18255  ereport(DEBUG1,
18256  (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
18257  RelationGetRelationName(scanrel))));
18258  else
18259  ereport(DEBUG1,
18260  (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
18261  RelationGetRelationName(scanrel))));
18262  return;
18263  }
18264 
18265  /*
18266  * Constraints proved insufficient. For plain relations, queue a
18267  * validation item now; for partitioned tables, recurse to process each
18268  * partition.
18269  */
18270  if (scanrel->rd_rel->relkind == RELKIND_RELATION)
18271  {
18272  AlteredTableInfo *tab;
18273 
18274  /* Grab a work queue entry. */
18275  tab = ATGetQueueEntry(wqueue, scanrel);
18276  Assert(tab->partition_constraint == NULL);
18277  tab->partition_constraint = (Expr *) linitial(partConstraint);
18278  tab->validate_default = validate_default;
18279  }
18280  else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18281  {
18282  PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
18283  int i;
18284 
18285  for (i = 0; i < partdesc->nparts; i++)
18286  {
18287  Relation part_rel;
18288  List *thisPartConstraint;
18289 
18290  /*
18291  * This is the minimum lock we need to prevent deadlocks.
18292  */
18293  part_rel = table_open(partdesc->oids[i], AccessExclusiveLock);
18294 
18295  /*
18296  * Adjust the constraint for scanrel so that it matches this
18297  * partition's attribute numbers.
18298  */
18299  thisPartConstraint =
18300  map_partition_varattnos(partConstraint, 1,
18301  part_rel, scanrel);
18302 
18303  QueuePartitionConstraintValidation(wqueue, part_rel,
18304  thisPartConstraint,
18305  validate_default);
18306  table_close(part_rel, NoLock); /* keep lock till commit */
18307  }
18308  }
18309 }

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

17679 {
17680  Node *stmt = (Node *) arg;
17681  ObjectType reltype;
17682  HeapTuple tuple;
17683  Form_pg_class classform;
17684  AclResult aclresult;
17685  char relkind;
17686 
17687  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
17688  if (!HeapTupleIsValid(tuple))
17689  return; /* concurrently dropped */
17690  classform = (Form_pg_class) GETSTRUCT(tuple);
17691  relkind = classform->relkind;
17692 
17693  /* Must own relation. */
17694  if (!object_ownercheck(RelationRelationId, relid, GetUserId()))
17696 
17697  /* No system table modifications unless explicitly allowed. */
17698  if (!allowSystemTableMods && IsSystemClass(relid, classform))
17699  ereport(ERROR,
17700  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17701  errmsg("permission denied: \"%s\" is a system catalog",
17702  rv->relname)));
17703 
17704  /*
17705  * Extract the specified relation type from the statement parse tree.
17706  *
17707  * Also, for ALTER .. RENAME, check permissions: the user must (still)
17708  * have CREATE rights on the containing namespace.
17709  */
17710  if (IsA(stmt, RenameStmt))
17711  {
17712  aclresult = object_aclcheck(NamespaceRelationId, classform->relnamespace,
17713  GetUserId(), ACL_CREATE);
17714  if (aclresult != ACLCHECK_OK)
17715  aclcheck_error(aclresult, OBJECT_SCHEMA,
17716  get_namespace_name(classform->relnamespace));
17717  reltype = ((RenameStmt *) stmt)->renameType;
17718  }
17719  else if (IsA(stmt, AlterObjectSchemaStmt))
17720  reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
17721 
17722  else if (IsA(stmt, AlterTableStmt))
17723  reltype = ((AlterTableStmt *) stmt)->objtype;
17724  else
17725  {
17726  elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
17727  reltype = OBJECT_TABLE; /* placate compiler */
17728  }
17729 
17730  /*
17731  * For compatibility with prior releases, we allow ALTER TABLE to be used
17732  * with most other types of relations (but not composite types). We allow
17733  * similar flexibility for ALTER INDEX in the case of RENAME, but not
17734  * otherwise. Otherwise, the user must select the correct form of the
17735  * command for the relation at issue.
17736  */
17737  if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
17738  ereport(ERROR,
17739  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17740  errmsg("\"%s\" is not a sequence", rv->relname)));
17741 
17742  if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
17743  ereport(ERROR,
17744  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17745  errmsg("\"%s\" is not a view", rv->relname)));
17746 
17747  if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
17748  ereport(ERROR,
17749  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17750  errmsg("\"%s\" is not a materialized view", rv->relname)));
17751 
17752  if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
17753  ereport(ERROR,
17754  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17755  errmsg("\"%s\" is not a foreign table", rv->relname)));
17756 
17757  if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
17758  ereport(ERROR,
17759  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17760  errmsg("\"%s\" is not a composite type", rv->relname)));
17761 
17762  if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
17763  relkind != RELKIND_PARTITIONED_INDEX
17764  && !IsA(stmt, RenameStmt))
17765  ereport(ERROR,
17766  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17767  errmsg("\"%s\" is not an index", rv->relname)));
17768 
17769  /*
17770  * Don't allow ALTER TABLE on composite types. We want people to use ALTER
17771  * TYPE for that.
17772  */
17773  if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
17774  ereport(ERROR,
17775  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17776  errmsg("\"%s\" is a composite type", rv->relname),
17777  /* translator: %s is an SQL ALTER command */
17778  errhint("Use %s instead.",
17779  "ALTER TYPE")));
17780 
17781  /*
17782  * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
17783  * to a different schema, such as indexes and TOAST tables.
17784  */
17786  {
17787  if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
17788  ereport(ERROR,
17789  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17790  errmsg("cannot change schema of index \"%s\"",
17791  rv->relname),
17792  errhint("Change the schema of the table instead.")));
17793  else if (relkind == RELKIND_COMPOSITE_TYPE)
17794  ereport(ERROR,
17795  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17796  errmsg("cannot change schema of composite type \"%s\"",
17797  rv->relname),
17798  /* translator: %s is an SQL ALTER command */
17799  errhint("Use %s instead.",
17800  "ALTER TYPE")));
17801  else if (relkind == RELKIND_TOASTVALUE)
17802  ereport(ERROR,
17803  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17804  errmsg("cannot change schema of TOAST table \"%s\"",
17805  rv->relname),
17806  errhint("Change the schema of the table instead.")));
17807  }
17808 
17809  ReleaseSysCache(tuple);
17810 }
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition: catalog.c:85
ObjectType
Definition: parsenodes.h:2260
@ OBJECT_FOREIGN_TABLE
Definition: parsenodes.h:2279
@ OBJECT_VIEW
Definition: parsenodes.h:2312
@ OBJECT_TYPE
Definition: parsenodes.h:2310

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

19557 {
19559  Form_pg_class classform;
19560  HeapTuple tuple;
19561 
19562  state = (struct AttachIndexCallbackState *) arg;
19563 
19564  if (!state->lockedParentTbl)
19565  {
19566  LockRelationOid(state->parentTblOid, AccessShareLock);
19567  state->lockedParentTbl = true;
19568  }
19569 
19570  /*
19571  * If we previously locked some other heap, and the name we're looking up
19572  * no longer refers to an index on that relation, release the now-useless
19573  * lock. XXX maybe we should do *after* we verify whether the index does
19574  * not actually belong to the same relation ...
19575  */
19576  if (relOid != oldRelOid && OidIsValid(state->partitionOid))
19577  {
19578  UnlockRelationOid(state->partitionOid, AccessShareLock);
19579  state->partitionOid = InvalidOid;
19580  }
19581 
19582  /* Didn't find a relation, so no need for locking or permission checks. */
19583  if (!OidIsValid(relOid))
19584  return;
19585 
19586  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
19587  if (!HeapTupleIsValid(tuple))
19588  return; /* concurrently dropped, so nothing to do */
19589  classform = (Form_pg_class) GETSTRUCT(tuple);
19590  if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
19591  classform->relkind != RELKIND_INDEX)
19592  ereport(ERROR,
19593  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
19594  errmsg("\"%s\" is not an index", rv->relname)));
19595  ReleaseSysCache(tuple);
19596 
19597  /*
19598  * Since we need only examine the heap's tupledesc, an access share lock
19599  * on it (preventing any DDL) is sufficient.
19600  */
19601  state->partitionOid = IndexGetRelation(relOid, false);
19602  LockRelationOid(state->partitionOid, AccessShareLock);
19603 }
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 1601 of file tablecmds.c.

1603 {
1604  HeapTuple tuple;
1606  char expected_relkind;
1607  bool is_partition;
1608  Form_pg_class classform;
1610  bool invalid_system_index = false;
1611 
1612  state = (struct DropRelationCallbackState *) arg;
1613  heap_lockmode = state->heap_lockmode;
1614 
1615  /*
1616  * If we previously locked some other index's heap, and the name we're
1617  * looking up no longer refers to that relation, release the now-useless
1618  * lock.
1619  */
1620  if (relOid != oldRelOid && OidIsValid(state->heapOid))
1621  {
1623  state->heapOid = InvalidOid;
1624  }
1625 
1626  /*
1627  * Similarly, if we previously locked some other partition's heap, and the
1628  * name we're looking up no longer refers to that relation, release the
1629  * now-useless lock.
1630  */
1631  if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1632  {
1633  UnlockRelationOid(state->partParentOid, AccessExclusiveLock);
1634  state->partParentOid = InvalidOid;
1635  }
1636 
1637  /* Didn't find a relation, so no need for locking or permission checks. */
1638  if (!OidIsValid(relOid))
1639  return;
1640 
1641  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1642  if (!HeapTupleIsValid(tuple))
1643  return; /* concurrently dropped, so nothing to do */
1644  classform = (Form_pg_class) GETSTRUCT(tuple);
1645  is_partition = classform->relispartition;
1646 
1647  /* Pass back some data to save lookups in RemoveRelations */
1648  state->actual_relkind = classform->relkind;
1649  state->actual_relpersistence = classform->relpersistence;
1650 
1651  /*
1652  * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1653  * but RemoveRelations() can only pass one relkind for a given relation.
1654  * It chooses RELKIND_RELATION for both regular and partitioned tables.
1655  * That means we must be careful before giving the wrong type error when
1656  * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1657  * exists with indexes.
1658  */
1659  if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1660  expected_relkind = RELKIND_RELATION;
1661  else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1662  expected_relkind = RELKIND_INDEX;
1663  else
1664  expected_relkind = classform->relkind;
1665 
1666  if (state->expected_relkind != expected_relkind)
1667  DropErrorMsgWrongType(rel->relname, classform->relkind,
1668  state->expected_relkind);
1669 
1670  /* Allow DROP to either table owner or schema owner */
1671  if (!object_ownercheck(RelationRelationId, relOid, GetUserId()) &&
1672  !object_ownercheck(NamespaceRelationId, classform->relnamespace, GetUserId()))
1674  get_relkind_objtype(classform->relkind),
1675  rel->relname);
1676 
1677  /*
1678  * Check the case of a system index that might have been invalidated by a
1679  * failed concurrent process and allow its drop. For the time being, this
1680  * only concerns indexes of toast relations that became invalid during a
1681  * REINDEX CONCURRENTLY process.
1682  */
1683  if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1684  {
1685  HeapTuple locTuple;
1686  Form_pg_index indexform;
1687  bool indisvalid;
1688 
1689  locTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(relOid));
1690  if (!HeapTupleIsValid(locTuple))
1691  {
1692  ReleaseSysCache(tuple);
1693  return;
1694  }
1695 
1696  indexform = (Form_pg_index) GETSTRUCT(locTuple);
1697  indisvalid = indexform->indisvalid;
1698  ReleaseSysCache(locTuple);
1699 
1700  /* Mark object as being an invalid index of system catalogs */
1701  if (!indisvalid)
1702  invalid_system_index = true;
1703  }
1704 
1705  /* In the case of an invalid index, it is fine to bypass this check */
1706  if (!invalid_system_index && !allowSystemTableMods && IsSystemClass(relOid, classform))
1707  ereport(ERROR,
1708  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
1709  errmsg("permission denied: \"%s\" is a system catalog",
1710  rel->relname)));
1711 
1712  ReleaseSysCache(tuple);
1713 
1714  /*
1715  * In DROP INDEX, attempt to acquire lock on the parent table before
1716  * locking the index. index_drop() will need this anyway, and since
1717  * regular queries lock tables before their indexes, we risk deadlock if
1718  * we do it the other way around. No error if we don't find a pg_index
1719  * entry, though --- the relation may have been dropped. Note that this
1720  * code will execute for either plain or partitioned indexes.
1721  */
1722  if (expected_relkind == RELKIND_INDEX &&
1723  relOid != oldRelOid)
1724  {
1725  state->heapOid = IndexGetRelation(relOid, true);
1726  if (OidIsValid(state->heapOid))
1727  LockRelationOid(state->heapOid, heap_lockmode);
1728  }
1729 
1730  /*
1731  * Similarly, if the relation is a partition, we must acquire lock on its
1732  * parent before locking the partition. That's because queries lock the
1733  * parent before its partitions, so we risk deadlock if we do it the other
1734  * way around.
1735  */
1736  if (is_partition && relOid != oldRelOid)
1737  {
1738  state->partParentOid = get_partition_parent(relOid, true);
1739  if (OidIsValid(state->partParentOid))
1740  LockRelationOid(state->partParentOid, AccessExclusiveLock);
1741  }
1742 }
static void DropErrorMsgWrongType(const char *relname, char wrongkind, char rightkind)
Definition: tablecmds.c:1410

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

3825 {
3826  HeapTuple tuple;
3827  Form_pg_class form;
3828 
3829  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
3830  if (!HeapTupleIsValid(tuple))
3831  return; /* concurrently dropped */
3832  form = (Form_pg_class) GETSTRUCT(tuple);
3833  renameatt_check(relid, form, false);
3834  ReleaseSysCache(tuple);
3835 }
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition: tablecmds.c:3629

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

17623 {
17624  HeapTuple tuple;
17625 
17626  /* Nothing to do if the relation was not found. */
17627  if (!OidIsValid(relId))
17628  return;
17629 
17630  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
17631  if (!HeapTupleIsValid(tuple)) /* should not happen */
17632  elog(ERROR, "cache lookup failed for relation %u", relId);
17633 
17634  truncate_check_rel(relId, (Form_pg_class) GETSTRUCT(tuple));
17635  truncate_check_perms(relId, (Form_pg_class) GETSTRUCT(tuple));
17636 
17637  ReleaseSysCache(tuple);
17638 }

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

17587 {
17588  char relkind;
17589  AclResult aclresult;
17590 
17591  /* Nothing to do if the relation was not found. */
17592  if (!OidIsValid(relId))
17593  return;
17594 
17595  /*
17596  * If the relation does exist, check whether it's an index. But note that
17597  * the relation might have been dropped between the time we did the name
17598  * lookup and now. In that case, there's nothing to do.
17599  */
17600  relkind = get_rel_relkind(relId);
17601  if (!relkind)
17602  return;
17603  if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
17604  relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
17605  ereport(ERROR,
17606  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
17607  errmsg("\"%s\" is not a table or materialized view", relation->relname)));
17608 
17609  /* Check permissions */
17610  aclresult = pg_class_aclcheck(relId, GetUserId(), ACL_MAINTAIN);
17611  if (aclresult != ACLCHECK_OK)
17612  aclcheck_error(aclresult,
17614  relation->relname);
17615 }
#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 17645 of file tablecmds.c.

17647 {
17648  HeapTuple tuple;
17649 
17650  /* Nothing to do if the relation was not found. */
17651  if (!OidIsValid(relId))
17652  return;
17653 
17654  tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
17655  if (!HeapTupleIsValid(tuple)) /* should not happen */
17656  elog(ERROR, "cache lookup failed for relation %u", relId);
17657 
17658  if (!object_ownercheck(RelationRelationId, relId, GetUserId()))
17660  relation->relname);
17661 
17662  if (!allowSystemTableMods &&
17663  IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
17664  ereport(ERROR,
17665  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
17666  errmsg("permission denied: \"%s\" is a system catalog",
17667  relation->relname)));
17668 
17669  ReleaseSysCache(tuple);
17670 }

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

14088 {
14089  CommentStmt *cmd;
14090  char *comment_str;
14091  AlterTableCmd *newcmd;
14092 
14093  /* Look for comment for object wanted, and leave if none */
14094  comment_str = GetComment(objid, ConstraintRelationId, 0);
14095  if (comment_str == NULL)
14096  return;
14097 
14098  /* Build CommentStmt node, copying all input data for safety */
14099  cmd = makeNode(CommentStmt);
14100  if (rel)
14101  {
14103  cmd->object = (Node *)
14106  makeString(pstrdup(conname)));
14107  }
14108  else
14109  {
14111  cmd->object = (Node *)
14113  makeString(pstrdup(conname)));
14114  }
14115  cmd->comment = comment_str;
14116 
14117  /* Append it to list of commands */
14118  newcmd = makeNode(AlterTableCmd);
14119  newcmd->subtype = AT_ReAddComment;
14120  newcmd->def = (Node *) cmd;
14121  tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
14122 }
TypeName * makeTypeNameFromNameList(List *names)
Definition: makefuncs.c:458
@ OBJECT_TABCONSTRAINT
Definition: parsenodes.h:2301
@ OBJECT_DOMCONSTRAINT
Definition: parsenodes.h:2274
#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:3262
ObjectType objtype
Definition: parsenodes.h:3260
Node * object
Definition: parsenodes.h:3261

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

19765 {
19766  Oid existingIdx;
19767 
19768  existingIdx = index_get_partition(partitionTbl,
19769  RelationGetRelid(parentIdx));
19770  if (OidIsValid(existingIdx))
19771  ereport(ERROR,
19772  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
19773  errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
19774  RelationGetRelationName(partIdx),
19775  RelationGetRelationName(parentIdx)),
19776  errdetail("Another index is already attached for partition \"%s\".",
19777  RelationGetRelationName(partitionTbl))));
19778 }

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

17353 {
17354  OnCommitItem *oc;
17355  MemoryContext oldcxt;
17356 
17357  /*
17358  * We needn't bother registering the relation unless there is an ON COMMIT
17359  * action we need to take.
17360  */
17362  return;
17363 
17365 
17366  oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
17367  oc->relid = relid;
17368  oc->oncommit = action;
17371 
17372  /*
17373  * We use lcons() here so that ON COMMIT actions are processed in reverse
17374  * order of registration. That might not be essential but it seems
17375  * reasonable.
17376  */
17377  on_commits = lcons(oc, on_commits);
17378 
17379  MemoryContextSwitchTo(oldcxt);
17380 }
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 16509 of file tablecmds.c.

16511 {
16512  Relation pg_index;
16513  Relation pg_class;
16514  HeapTuple pg_class_tuple;
16515  HeapTuple pg_index_tuple;
16516  Form_pg_class pg_class_form;
16517  Form_pg_index pg_index_form;
16518  ListCell *index;
16519 
16520  /*
16521  * Check whether relreplident has changed, and update it if so.
16522  */
16523  pg_class = table_open(RelationRelationId, RowExclusiveLock);
16524  pg_class_tuple = SearchSysCacheCopy1(RELOID,
16526  if (!HeapTupleIsValid(pg_class_tuple))
16527  elog(ERROR, "cache lookup failed for relation \"%s\"",
16529  pg_class_form = (Form_pg_class) GETSTRUCT(pg_class_tuple);
16530  if (pg_class_form->relreplident != ri_type)
16531  {
16532  pg_class_form->relreplident = ri_type;
16533  CatalogTupleUpdate(pg_class, &pg_class_tuple->t_self, pg_class_tuple);
16534  }
16535  table_close(pg_class, RowExclusiveLock);
16536  heap_freetuple(pg_class_tuple);
16537 
16538  /*
16539  * Update the per-index indisreplident flags correctly.
16540  */
16541  pg_index = table_open(IndexRelationId, RowExclusiveLock);
16542  foreach(index, RelationGetIndexList(rel))
16543  {
16544  Oid thisIndexOid = lfirst_oid(index);
16545  bool dirty = false;
16546 
16547  pg_index_tuple = SearchSysCacheCopy1(INDEXRELID,
16548  ObjectIdGetDatum(thisIndexOid));
16549  if (!HeapTupleIsValid(pg_index_tuple))
16550  elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
16551  pg_index_form = (Form_pg_index) GETSTRUCT(pg_index_tuple);
16552 
16553  if (thisIndexOid == indexOid)
16554  {
16555  /* Set the bit if not already set. */
16556  if (!pg_index_form->indisreplident)
16557  {
16558  dirty = true;
16559  pg_index_form->indisreplident = true;
16560  }
16561  }
16562  else
16563  {
16564  /* Unset the bit if set. */
16565  if (pg_index_form->indisreplident)
16566  {
16567  dirty = true;
16568  pg_index_form->indisreplident = false;
16569  }
16570  }
16571 
16572  if (dirty)
16573  {
16574  CatalogTupleUpdate(pg_index, &pg_index_tuple->t_self, pg_index_tuple);
16575  InvokeObjectPostAlterHookArg(IndexRelationId, thisIndexOid, 0,
16576  InvalidOid, is_internal);
16577 
16578  /*
16579  * Invalidate the relcache for the table, so that after we commit
16580  * all sessions will refresh the table's replica identity index
16581  * before attempting any UPDATE or DELETE on the table. (If we
16582  * changed the table's pg_class row above, then a relcache inval
16583  * is already queued due to that; but we might not have.)
16584  */
16586  }
16587  heap_freetuple(pg_index_tuple);
16588  }
16589 
16590  table_close(pg_index, RowExclusiveLock);
16591 }
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 13324 of file tablecmds.c.

13326 {
13327  Relation depRel;
13328  ScanKeyData key[3];
13329  SysScanDesc scan;
13330  HeapTuple depTup;
13331 
13332  Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
13333 
13334  depRel = table_open(DependRelationId, RowExclusiveLock);
13335 
13336  ScanKeyInit(&key[0],
13337  Anum_pg_depend_refclassid,
13338  BTEqualStrategyNumber, F_OIDEQ,
13339  ObjectIdGetDatum(RelationRelationId));
13340  ScanKeyInit(&key[1],
13341  Anum_pg_depend_refobjid,
13342  BTEqualStrategyNumber, F_OIDEQ,
13344  ScanKeyInit(&key[2],
13345  Anum_pg_depend_refobjsubid,
13346  BTEqualStrategyNumber, F_INT4EQ,
13348 
13349  scan = systable_beginscan(depRel, DependReferenceIndexId, true,
13350  NULL, 3, key);
13351 
13352  while (HeapTupleIsValid(depTup = systable_getnext(scan)))
13353  {
13354  Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
13355  ObjectAddress foundObject;
13356 
13357  foundObject.classId = foundDep->classid;
13358  foundObject.objectId = foundDep->objid;
13359  foundObject.objectSubId = foundDep->objsubid;
13360 
13361  switch (foundObject.classId)
13362  {
13363  case RelationRelationId:
13364  {
13365  char relKind = get_rel_relkind(foundObject.objectId);
13366 
13367  if (relKind == RELKIND_INDEX ||
13368  relKind == RELKIND_PARTITIONED_INDEX)
13369  {
13370  Assert(foundObject.objectSubId == 0);
13371  RememberIndexForRebuilding(foundObject.objectId, tab);
13372  }
13373  else if (relKind == RELKIND_SEQUENCE)
13374  {
13375  /*
13376  * This must be a SERIAL column's sequence. We need
13377  * not do anything to it.
13378  */
13379  Assert(foundObject.objectSubId == 0);
13380  }
13381  else
13382  {
13383  /* Not expecting any other direct dependencies... */
13384  elog(ERROR, "unexpected object depending on column: %s",
13385  getObjectDescription(&foundObject, false));
13386  }
13387  break;
13388  }
13389 
13390  case ConstraintRelationId:
13391  Assert(foundObject.objectSubId == 0);
13392  RememberConstraintForRebuilding(foundObject.objectId, tab);
13393  break;
13394 
13395  case ProcedureRelationId:
13396 
13397  /*
13398  * A new-style SQL function can depend on a column, if that
13399  * column is referenced in the parsed function body. Ideally
13400  * we'd automatically update the function by deparsing and
13401  * reparsing it, but that's risky and might well fail anyhow.
13402  * FIXME someday.
13403  *
13404  * This is only a problem for AT_AlterColumnType, not
13405  * AT_SetExpression.
13406  */
13407  if (subtype == AT_AlterColumnType)
13408  ereport(ERROR,
13409  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13410  errmsg("cannot alter type of a column used by a function or procedure"),
13411  errdetail("%s depends on column \"%s\"",
13412  getObjectDescription(&foundObject, false),
13413  colName)));
13414  break;
13415 
13416  case RewriteRelationId:
13417 
13418  /*
13419  * View/rule bodies have pretty much the same issues as
13420  * function bodies. FIXME someday.
13421  */
13422  if (subtype == AT_AlterColumnType)
13423  ereport(ERROR,
13424  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13425  errmsg("cannot alter type of a column used by a view or rule"),
13426  errdetail("%s depends on column \"%s\"",
13427  getObjectDescription(&foundObject, false),
13428  colName)));
13429  break;
13430 
13431  case TriggerRelationId:
13432 
13433  /*
13434  * A trigger can depend on a column because the column is
13435  * specified as an update target, or because the column is
13436  * used in the trigger's WHEN condition. The first case would
13437  * not require any extra work, but the second case would
13438  * require updating the WHEN expression, which has the same
13439  * issues as above. Since we can't easily tell which case
13440  * applies, we punt for both. FIXME someday.
13441  */
13442  if (subtype == AT_AlterColumnType)
13443  ereport(ERROR,
13444  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13445  errmsg("cannot alter type of a column used in a trigger definition"),
13446  errdetail("%s depends on column \"%s\"",
13447  getObjectDescription(&foundObject, false),
13448  colName)));
13449  break;
13450 
13451  case PolicyRelationId:
13452 
13453  /*
13454  * A policy can depend on a column because the column is
13455  * specified in the policy's USING or WITH CHECK qual
13456  * expressions. It might be possible to rewrite and recheck
13457  * the policy expression, but punt for now. It's certainly
13458  * easy enough to remove and recreate the policy; still, FIXME
13459  * someday.
13460  */
13461  if (subtype == AT_AlterColumnType)
13462  ereport(ERROR,
13463  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13464  errmsg("cannot alter type of a column used in a policy definition"),
13465  errdetail("%s depends on column \"%s\"",
13466  getObjectDescription(&foundObject, false),
13467  colName)));
13468  break;
13469 
13470  case AttrDefaultRelationId:
13471  {
13473 
13474  if (col.objectId == RelationGetRelid(rel) &&
13475  col.objectSubId == attnum)
13476  {
13477  /*
13478  * Ignore the column's own default expression. The
13479  * caller deals with it.
13480  */
13481  }
13482  else
13483  {
13484  /*
13485  * This must be a reference from the expression of a
13486  * generated column elsewhere in the same table.
13487  * Changing the type/generated expression of a column
13488  * that is used by a generated column is not allowed
13489  * by SQL standard, so just punt for now. It might be
13490  * doable with some thinking and effort.
13491  */
13492  if (subtype == AT_AlterColumnType)
13493  ereport(ERROR,
13494  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13495  errmsg("cannot alter type of a column used by a generated column"),
13496  errdetail("Column \"%s\" is used by generated column \"%s\".",
13497  colName,
13498  get_attname(col.objectId,
13499  col.objectSubId,
13500  false))));
13501  }
13502  break;
13503  }
13504 
13505  case StatisticExtRelationId:
13506 
13507  /*
13508  * Give the extended-stats machinery a chance to fix anything
13509  * that this column type change would break.
13510  */
13511  RememberStatisticsForRebuilding(foundObject.objectId, tab);
13512  break;
13513 
13514  case PublicationRelRelationId:
13515 
13516  /*
13517  * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
13518  * clause. Same issues as above. FIXME someday.
13519  */
13520  if (subtype == AT_AlterColumnType)
13521  ereport(ERROR,
13522  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
13523  errmsg("cannot alter type of a column used by a publication WHERE clause"),
13524  errdetail("%s depends on column \"%s\"",
13525  getObjectDescription(&foundObject, false),
13526  colName)));
13527  break;
13528 
13529  default:
13530 
13531  /*
13532  * We don't expect any other sorts of objects to depend on a
13533  * column.
13534  */
13535  elog(ERROR, "unexpected object depending on column: %s",
13536  getObjectDescription(&foundObject, false));
13537  break;
13538  }
13539  }
13540 
13541  systable_endscan(scan);
13542  table_close(depRel, NoLock);
13543 }
ObjectAddress GetAttrDefaultColumnAddress(Oid attrdefoid)
Definition: pg_attrdef.c:381
static void RememberIndexForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:13621
static void RememberStatisticsForRebuilding(Oid stxoid, AlteredTableInfo *tab)
Definition: tablecmds.c:13672
static void RememberConstraintForRebuilding(Oid conoid, AlteredTableInfo *tab)
Definition: tablecmds.c:13581

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

13566 {
13567  if (!get_index_isclustered(indoid))
13568  return;
13569 
13570  if (tab->clusterOnIndex)
13571  elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
13572 
13573  tab->clusterOnIndex = get_rel_name(indoid);
13574 }
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 13581 of file tablecmds.c.

13582 {
13583  /*
13584  * This de-duplication check is critical for two independent reasons: we
13585  * mustn't try to recreate the same constraint twice, and if a constraint
13586  * depends on more than one column whose type is to be altered, we must
13587  * capture its definition string before applying any of the column type
13588  * changes. ruleutils.c will get confused if we ask again later.
13589  */
13590  if (!list_member_oid(tab->changedConstraintOids, conoid))
13591  {
13592  /* OK, capture the constraint's existing definition string */
13593  char *defstring = pg_get_constraintdef_command(conoid);
13594  Oid indoid;
13595 
13597  conoid);
13599  defstring);
13600 
13601  /*
13602  * For the index of a constraint, if any, remember if it is used for
13603  * the table's replica identity or if it is a clustered index, so that
13604  * ATPostAlterTypeCleanup() can queue up commands necessary to restore
13605  * those properties.
13606  */
13607  indoid = get_constraint_index(conoid);
13608  if (OidIsValid(indoid))
13609  {
13611  RememberClusterOnForRebuilding(indoid, tab);
13612  }
13613  }
13614 }
char * pg_get_constraintdef_command(Oid constraintId)
Definition: ruleutils.c:2168
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:13550
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)
Definition: tablecmds.c:13565

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

13622 {
13623  /*
13624  * This de-duplication check is critical for two independent reasons: we
13625  * mustn't try to recreate the same index twice, and if an index depends
13626  * on more than one column whose type is to be altered, we must capture
13627  * its definition string before applying any of the column type changes.
13628  * ruleutils.c will get confused if we ask again later.
13629  */
13630  if (!list_member_oid(tab->changedIndexOids, indoid))
13631  {
13632  /*
13633  * Before adding it as an index-to-rebuild, we'd better see if it
13634  * belongs to a constraint, and if so rebuild the constraint instead.
13635  * Typically this check fails, because constraint indexes normally
13636  * have only dependencies on their constraint. But it's possible for
13637  * such an index to also have direct dependencies on table columns,
13638  * for example with a partial exclusion constraint.
13639  */
13640  Oid conoid = get_index_constraint(indoid);
13641 
13642  if (OidIsValid(conoid))
13643  {
13644  RememberConstraintForRebuilding(conoid, tab);
13645  }
13646  else
13647  {
13648  /* OK, capture the index's existing definition string */
13649  char *defstring = pg_get_indexdef_string(indoid);
13650 
13652  indoid);
13654  defstring);
13655 
13656  /*
13657  * Remember if this index is used for the table's replica identity
13658  * or if it is a clustered index, so that ATPostAlterTypeCleanup()
13659  * can queue up commands necessary to restore those properties.
13660  */
13662  RememberClusterOnForRebuilding(indoid, tab);
13663  }
13664  }
13665 }
Oid get_index_constraint(Oid indexId)
Definition: pg_depend.c:989
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 13550 of file tablecmds.c.

13551 {
13552  if (!get_index_isreplident(indoid))
13553  return;
13554 
13555  if (tab->replicaIdentityIndex)
13556  elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
13557 
13558  tab->replicaIdentityIndex = get_rel_name(indoid);
13559 }
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 13672 of file tablecmds.c.

13673 {
13674  /*
13675  * This de-duplication check is critical for two independent reasons: we
13676  * mustn't try to recreate the same statistics object twice, and if the
13677  * statistics object depends on more than one column whose type is to be
13678  * altered, we must capture its definition string before applying any of
13679  * the type changes. ruleutils.c will get confused if we ask again later.
13680  */
13681  if (!list_member_oid(tab->changedStatisticsOids, stxoid))
13682  {
13683  /* OK, capture the statistics object's existing definition string */
13684  char *defstring = pg_get_statisticsobjdef_string(stxoid);
13685 
13687  stxoid);
13689  defstring);
13690  }
13691 }
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 17388 of file tablecmds.c.

17389 {
17390  ListCell *l;
17391 
17392  foreach(l, on_commits)
17393  {
17394  OnCommitItem *oc = (OnCommitItem *) lfirst(l);
17395 
17396  if (oc->relid == relid)
17397  {
17399  break;
17400  }
17401  }
17402 }

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

16104 {
16105  Relation catalogRelation;
16106  SysScanDesc scan;
16107  ScanKeyData key[3];
16108  HeapTuple attributeTuple,
16109  constraintTuple;
16110  List *connames;
16111  bool found;
16112  bool is_partitioning;
16113 
16114  is_partitioning = (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
16115 
16116  found = DeleteInheritsTuple(RelationGetRelid(child_rel),
16117  RelationGetRelid(parent_rel),
16118  expect_detached,
16119  RelationGetRelationName(child_rel));
16120  if (!found)
16121  {
16122  if (is_partitioning)
16123  ereport(ERROR,
16125  errmsg("relation \"%s\" is not a partition of relation \"%s\"",
16126  RelationGetRelationName(child_rel),
16127  RelationGetRelationName(parent_rel))));
16128  else
16129  ereport(ERROR,
16131  errmsg("relation \"%s\" is not a parent of relation \"%s\"",
16132  RelationGetRelationName(parent_rel),
16133  RelationGetRelationName(child_rel))));
16134  }
16135 
16136  /*
16137  * Search through child columns looking for ones matching parent rel
16138  */
16139  catalogRelation = table_open(AttributeRelationId, RowExclusiveLock);
16140  ScanKeyInit(&key[0],
16141  Anum_pg_attribute_attrelid,
16142  BTEqualStrategyNumber, F_OIDEQ,
16143  ObjectIdGetDatum(RelationGetRelid(child_rel)));
16144  scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
16145  true, NULL, 1, key);
16146  while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
16147  {
16148  Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
16149 
16150  /* Ignore if dropped or not inherited */
16151  if (att->attisdropped)
16152  continue;
16153  if (att->attinhcount <= 0)
16154  continue;
16155 
16157  NameStr(att->attname)))
16158  {
16159  /* Decrement inhcount and possibly set islocal to true */
16160  HeapTuple copyTuple = heap_copytuple(attributeTuple);
16161  Form_pg_attribute copy_att = (Form_pg_attribute) GETSTRUCT(copyTuple);
16162 
16163  copy_att->attinhcount--;
16164  if (copy_att->attinhcount == 0)
16165  copy_att->attislocal = true;
16166 
16167  CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16168  heap_freetuple(copyTuple);
16169  }
16170  }
16171  systable_endscan(scan);
16172  table_close(catalogRelation, RowExclusiveLock);
16173 
16174  /*
16175  * Likewise, find inherited check constraints and disinherit them. To do
16176  * this, we first need a list of the names of the parent's check
16177  * constraints. (We cheat a bit by only checking for name matches,
16178  * assuming that the expressions will match.)
16179  */
16180  catalogRelation = table_open(ConstraintRelationId, RowExclusiveLock);
16181  ScanKeyInit(&key[0],
16182  Anum_pg_constraint_conrelid,
16183  BTEqualStrategyNumber, F_OIDEQ,
16184  ObjectIdGetDatum(RelationGetRelid(parent_rel)));
16185  scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16186  true, NULL, 1, key);
16187 
16188  connames = NIL;
16189 
16190  while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16191  {
16192  Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16193 
16194  if (con->contype == CONSTRAINT_CHECK)
16195  connames = lappend(connames, pstrdup(NameStr(con->conname)));
16196  }
16197 
16198  systable_endscan(scan);
16199 
16200  /* Now scan the child's constraints */
16201  ScanKeyInit(&key[0],
16202  Anum_pg_constraint_conrelid,
16203  BTEqualStrategyNumber, F_OIDEQ,
16204  ObjectIdGetDatum(RelationGetRelid(child_rel)));
16205  scan = systable_beginscan(catalogRelation, ConstraintRelidTypidNameIndexId,
16206  true, NULL, 1, key);
16207 
16208  while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
16209  {
16210  Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(constraintTuple);
16211  bool match;
16212 
16213  if (con->contype != CONSTRAINT_CHECK)
16214  continue;
16215 
16216  match = false;
16217  foreach_ptr(char, chkname, connames)
16218  {
16219  if (strcmp(NameStr(con->conname), chkname) == 0)
16220  {
16221  match = true;
16222  break;
16223  }
16224  }
16225 
16226  if (match)
16227  {
16228  /* Decrement inhcount and possibly set islocal to true */
16229  HeapTuple copyTuple = heap_copytuple(constraintTuple);
16230  Form_pg_constraint copy_con = (Form_pg_constraint) GETSTRUCT(copyTuple);
16231 
16232  if (copy_con->coninhcount <= 0) /* shouldn't happen */
16233  elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
16234  RelationGetRelid(child_rel), NameStr(copy_con->conname));
16235 
16236  copy_con->coninhcount--;
16237  if (copy_con->coninhcount == 0)
16238  copy_con->conislocal = true;
16239 
16240  CatalogTupleUpdate(catalogRelation, &copyTuple->t_self, copyTuple);
16241  heap_freetuple(copyTuple);
16242  }
16243  }
16244 
16245  systable_endscan(scan);
16246  table_close(catalogRelation, RowExclusiveLock);
16247 
16249  RelationRelationId,
16250  RelationGetRelid(parent_rel),
16251  child_dependency_type(is_partitioning));
16252 
16253  /*
16254  * Post alter hook of this inherits. Since object_access_hook doesn't take
16255  * multiple object identifiers, we relay oid of parent relation using
16256  * auxiliary_id argument.
16257  */
16258  InvokeObjectPostAlterHookArg(InheritsRelationId,
16259  RelationGetRelid(child_rel), 0,
16260  RelationGetRelid(parent_rel), false);
16261 }
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:353

References BTEqualStrategyNumber, CatalogTupleUpdate(), child_dependency_type, DeleteInheritsTuple(), drop_parent_dependency(), elog, ereport, errcode(), ERRCODE_UNDEFINED_TABLE, errmsg(), ERROR, foreach_ptr, GETSTRUCT, heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHookArg, sort-test::key, lappend(), 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 1437 of file tablecmds.c.

1438 {
1439  ObjectAddresses *objects;
1440  char relkind;
1441  ListCell *cell;
1442  int flags = 0;
1443  LOCKMODE lockmode = AccessExclusiveLock;
1444 
1445  /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1446  if (drop->concurrent)
1447  {
1448  /*
1449  * Note that for temporary relations this lock may get upgraded later
1450  * on, but as no other session can access a temporary relation, this
1451  * is actually fine.
1452  */
1453  lockmode = ShareUpdateExclusiveLock;
1454  Assert(drop->removeType == OBJECT_INDEX);
1455  if (list_length(drop->objects) != 1)
1456  ereport(ERROR,
1457  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1458  errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1459  if (drop->behavior == DROP_CASCADE)
1460  ereport(ERROR,
1461  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1462  errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1463  }
1464 
1465  /*
1466  * First we identify all the relations, then we delete them in a single
1467  * performMultipleDeletions() call. This is to avoid unwanted DROP
1468  * RESTRICT errors if one of the relations depends on another.
1469  */
1470 
1471  /* Determine required relkind */
1472  switch (drop->removeType)
1473  {
1474  case OBJECT_TABLE:
1475  relkind = RELKIND_RELATION;
1476  break;
1477 
1478  case OBJECT_INDEX:
1479  relkind = RELKIND_INDEX;
1480  break;
1481 
1482  case OBJECT_SEQUENCE:
1483  relkind = RELKIND_SEQUENCE;
1484  break;
1485 
1486  case OBJECT_VIEW:
1487  relkind = RELKIND_VIEW;
1488  break;
1489 
1490  case OBJECT_MATVIEW:
1491  relkind = RELKIND_MATVIEW;
1492  break;
1493 
1494  case OBJECT_FOREIGN_TABLE:
1495  relkind = RELKIND_FOREIGN_TABLE;
1496  break;
1497 
1498  default:
1499  elog(ERROR, "unrecognized drop object type: %d",
1500  (int) drop->removeType);
1501  relkind = 0; /* keep compiler quiet */
1502  break;
1503  }
1504 
1505  /* Lock and validate each relation; build a list of object addresses */
1506  objects = new_object_addresses();
1507 
1508  foreach(cell, drop->objects)
1509  {
1510  RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1511  Oid relOid;
1512  ObjectAddress obj;
1514 
1515  /*
1516  * These next few steps are a great deal like relation_openrv, but we
1517  * don't bother building a relcache entry since we don't need it.
1518  *
1519  * Check for shared-cache-inval messages before trying to access the
1520  * relation. This is needed to cover the case where the name
1521  * identifies a rel that has been dropped and recreated since the
1522  * start of our transaction: if we don't flush the old syscache entry,
1523  * then we'll latch onto that entry and suffer an error later.
1524  */
1526 
1527  /* Look up the appropriate relation using namespace search. */
1528  state.expected_relkind = relkind;
1529  state.heap_lockmode = drop->concurrent ?
1531  /* We must initialize these fields to show that no locks are held: */
1532  state.heapOid = InvalidOid;
1533  state.partParentOid = InvalidOid;
1534 
1535  relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1537  (void *) &state);
1538 
1539  /* Not there? */
1540  if (!OidIsValid(relOid))
1541  {
1542  DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1543  continue;
1544  }
1545 
1546  /*
1547  * Decide if concurrent mode needs to be used here or not. The
1548  * callback retrieved the rel's persistence for us.
1549  */
1550  if (drop->concurrent &&
1551  state.actual_relpersistence != RELPERSISTENCE_TEMP)
1552  {
1553  Assert(list_length(drop->objects) == 1 &&
1554  drop->removeType == OBJECT_INDEX);
1556  }
1557 
1558  /*
1559  * Concurrent index drop cannot be used with partitioned indexes,
1560  * either.
1561  */
1562  if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1563  state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1564  ereport(ERROR,
1565  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1566  errmsg("cannot drop partitioned index \"%s\" concurrently",
1567  rel->relname)));
1568 
1569  /*
1570  * If we're told to drop a partitioned index, we must acquire lock on
1571  * all the children of its parent partitioned table before proceeding.
1572  * Otherwise we'd try to lock the child index partitions before their
1573  * tables, leading to potential deadlock against other sessions that
1574  * will lock those objects in the other order.
1575  */
1576  if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1577  (void) find_all_inheritors(state.heapOid,
1578  state.heap_lockmode,
1579  NULL);
1580 
1581  /* OK, we're ready to delete this one */
1582  obj.classId = RelationRelationId;
1583  obj.objectId = relOid;
1584  obj.objectSubId = 0;
1585 
1586  add_exact_object_address(&obj, objects);
1587  }
1588 
1589  performMultipleDeletions(objects, drop->behavior, flags);
1590 
1591  free_object_addresses(objects);
1592 }
#define PERFORM_DELETION_CONCURRENTLY
Definition: dependency.h:93
void AcceptInvalidationMessages(void)
Definition: inval.c:806
RangeVar * makeRangeVarFromNameList(const List *names)
Definition: namespace.c:3539
bool missing_ok
Definition: parsenodes.h:3237
List * objects
Definition: parsenodes.h:3234
ObjectType removeType
Definition: parsenodes.h:3235
bool concurrent
Definition: parsenodes.h:3238
DropBehavior behavior
Definition: parsenodes.h:3236
static void DropErrorMsgNonExistent(RangeVar *rel, char rightkind, bool missing_ok)
Definition: tablecmds.c:1362
static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
Definition: tablecmds.c:1601

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

3888 {
3889  Relation targetrelation = NULL;
3890  Oid constraintOid;
3891  HeapTuple tuple;
3892  Form_pg_constraint con;
3893  ObjectAddress address;
3894 
3895  Assert(!myrelid || !mytypid);
3896 
3897  if (mytypid)
3898  {
3899  constraintOid = get_domain_constraint_oid(mytypid, oldconname, false);
3900  }
3901  else
3902  {
3903  targetrelation = relation_open(myrelid, AccessExclusiveLock);
3904 
3905  /*
3906  * don't tell it whether we're recursing; we allow changing typed
3907  * tables here
3908  */
3909  renameatt_check(myrelid, RelationGetForm(targetrelation), false);
3910 
3911  constraintOid = get_relation_constraint_oid(myrelid, oldconname, false);
3912  }
3913 
3914  tuple = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintOid));
3915  if (!HeapTupleIsValid(tuple))
3916  elog(ERROR, "cache lookup failed for constraint %u",
3917  constraintOid);
3918  con = (Form_pg_constraint) GETSTRUCT(tuple);
3919 
3920  if (myrelid && con->contype == CONSTRAINT_CHECK && !con->connoinherit)
3921  {
3922  if (recurse)
3923  {
3924  List *child_oids,
3925  *child_numparents;
3926  ListCell *lo,
3927  *li;
3928 
3929  child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3930  &child_numparents);
3931 
3932  forboth(lo, child_oids, li, child_numparents)
3933  {
3934  Oid childrelid = lfirst_oid(lo);
3935  int numparents = lfirst_int(li);
3936 
3937  if (childrelid == myrelid)
3938  continue;
3939 
3940  rename_constraint_internal(childrelid, InvalidOid, oldconname, newconname, false, true, numparents);
3941  }
3942  }
3943  else
3944  {
3945  if (expected_parents == 0 &&
3946  find_inheritance_children(myrelid, NoLock) != NIL)
3947  ereport(ERROR,
3948  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3949  errmsg("inherited constraint \"%s\" must be renamed in child tables too",
3950  oldconname)));
3951  }
3952 
3953  if (con->coninhcount > expected_parents)
3954  ereport(ERROR,
3955  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3956  errmsg("cannot rename inherited constraint \"%s\"",
3957  oldconname)));
3958  }
3959 
3960  if (con->conindid
3961  && (con->contype == CONSTRAINT_PRIMARY
3962  || con->contype == CONSTRAINT_UNIQUE
3963  || con->contype == CONSTRAINT_EXCLUSION))
3964  /* rename the index; this renames the constraint as well */
3965  RenameRelationInternal(con->conindid, newconname, false, true);
3966  else
3967  RenameConstraintById(constraintOid, newconname);
3968 
3969  ObjectAddressSet(address, ConstraintRelationId, constraintOid);
3970 
3971  ReleaseSysCache(tuple);
3972 
3973  if (targetrelation)
3974  {
3975  /*
3976  * Invalidate relcache so as others can see the new constraint name.
3977  */
3978  CacheInvalidateRelcache(targetrelation);
3979 
3980  relation_close(targetrelation, NoLock); /* close rel but keep lock */
3981  }
3982 
3983  return address;
3984 }
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:3881

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

3844 {
3845  Oid relid;
3847  ObjectAddress address;
3848 
3849  /* lock level taken here should match renameatt_internal */
3851  stmt->missing_ok ? RVR_MISSING_OK : 0,
3853  NULL);
3854 
3855  if (!OidIsValid(relid))
3856  {
3857  ereport(NOTICE,
3858  (errmsg("relation \"%s\" does not exist, skipping",
3859  stmt->relation->relname)));
3860  return InvalidObjectAddress;
3861  }
3862 
3863  attnum =
3864  renameatt_internal(relid,
3865  stmt->subname, /* old att name */
3866  stmt->newname, /* new att name */
3867  stmt->relation->inh, /* recursive? */
3868  false, /* recursing? */
3869  0, /* expected inhcount */
3870  stmt->behavior);
3871 
3872  ObjectAddressSubSet(address, RelationRelationId, relid, attnum);
3873 
3874  return address;
3875 }
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition: tablecmds.c:3678
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition: tablecmds.c:3823

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

3630 {
3631  char relkind = classform->relkind;
3632 
3633  if (classform->reloftype && !recursing)
3634  ereport(ERROR,
3635  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3636  errmsg("cannot rename column of typed table")));
3637 
3638  /*
3639  * Renaming the columns of sequences or toast tables doesn't actually
3640  * break anything from the system's point of view, since internal
3641  * references are by attnum. But it doesn't seem right to allow users to
3642  * change names that are hardcoded into the system, hence the following
3643  * restriction.
3644  */
3645  if (relkind != RELKIND_RELATION &&
3646  relkind != RELKIND_VIEW &&
3647  relkind != RELKIND_MATVIEW &&
3648  relkind != RELKIND_COMPOSITE_TYPE &&
3649  relkind != RELKIND_INDEX &&
3650  relkind != RELKIND_PARTITIONED_INDEX &&
3651  relkind != RELKIND_FOREIGN_TABLE &&
3652  relkind != RELKIND_PARTITIONED_TABLE)
3653  ereport(ERROR,
3654  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
3655  errmsg("cannot rename columns of relation \"%s\"",
3656  NameStr(classform->relname)),
3658 
3659  /*
3660  * permissions checking. only the owner of a class can change its schema.
3661  */
3662  if (!object_ownercheck(RelationRelationId, myrelid, GetUserId()))
3664  NameStr(classform->relname));
3665  if (!allowSystemTableMods && IsSystemClass(myrelid, classform))
3666  ereport(ERROR,
3667  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
3668  errmsg("permission denied: \"%s\" is a system catalog",
3669  NameStr(classform->relname))));
3670 }

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

3685 {
3686  Relation targetrelation;
3687  Relation attrelation;
3688  HeapTuple atttup;
3689  Form_pg_attribute attform;
3691 
3692  /*
3693  * Grab an exclusive lock on the target table, which we will NOT release
3694  * until end of transaction.
3695  */
3696  targetrelation = relation_open(myrelid, AccessExclusiveLock);
3697  renameatt_check(myrelid, RelationGetForm(targetrelation), recursing);
3698 
3699  /*
3700  * if the 'recurse' flag is set then we are supposed to rename this
3701  * attribute in all classes that inherit from 'relname' (as well as in
3702  * 'relname').
3703  *
3704  * any permissions or problems with duplicate attributes will cause the
3705  * whole transaction to abort, which is what we want -- all or nothing.
3706  */
3707  if (recurse)
3708  {
3709  List *child_oids,
3710  *child_numparents;
3711  ListCell *lo,
3712  *li;
3713 
3714  /*
3715  * we need the number of parents for each child so that the recursive
3716  * calls to renameatt() can determine whether there are any parents
3717  * outside the inheritance hierarchy being processed.
3718  */
3719  child_oids = find_all_inheritors(myrelid, AccessExclusiveLock,
3720  &child_numparents);
3721 
3722  /*
3723  * find_all_inheritors does the recursive search of the inheritance
3724  * hierarchy, so all we have to do is process all of the relids in the
3725  * list that it returns.
3726  */
3727  forboth(lo, child_oids, li, child_numparents)
3728  {
3729  Oid childrelid = lfirst_oid(lo);
3730  int numparents = lfirst_int(li);
3731 
3732  if (childrelid == myrelid)
3733  continue;
3734  /* note we need not recurse again */
3735  renameatt_internal(childrelid, oldattname, newattname, false, true, numparents, behavior);
3736  }
3737  }
3738  else
3739  {
3740  /*
3741  * If we are told not to recurse, there had better not be any child
3742  * tables; else the rename would put them out of step.
3743  *
3744  * expected_parents will only be 0 if we are not already recursing.
3745  */
3746  if (expected_parents == 0 &&
3747  find_inheritance_children(myrelid, NoLock) != NIL)
3748  ereport(ERROR,
3749  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3750  errmsg("inherited column \"%s\" must be renamed in child tables too",
3751  oldattname)));
3752  }
3753 
3754  /* rename attributes in typed tables of composite type */
3755  if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3756  {
3757  List *child_oids;
3758  ListCell *lo;
3759 
3760  child_oids = find_typed_table_dependencies(targetrelation->rd_rel->reltype,
3761  RelationGetRelationName(targetrelation),
3762  behavior);
3763 
3764  foreach(lo, child_oids)
3765  renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3766  }
3767 
3768  attrelation = table_open(AttributeRelationId, RowExclusiveLock);
3769 
3770  atttup = SearchSysCacheCopyAttName(myrelid, oldattname);
3771  if (!HeapTupleIsValid(atttup))
3772  ereport(ERROR,
3773  (errcode(ERRCODE_UNDEFINED_COLUMN),
3774  errmsg("column \"%s\" does not exist",
3775  oldattname)));
3776  attform = (Form_pg_attribute) GETSTRUCT(atttup);
3777 
3778  attnum = attform->attnum;
3779  if (attnum <= 0)
3780  ereport(ERROR,
3781  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3782  errmsg("cannot rename system column \"%s\"",
3783  oldattname)));
3784 
3785  /*
3786  * if the attribute is inherited, forbid the renaming. if this is a
3787  * top-level call to renameatt(), then expected_parents will be 0, so the
3788  * effect of this code will be to prohibit the renaming if the attribute
3789  * is inherited at all. if this is a recursive call to renameatt(),
3790  * expected_parents will be the number of parents the current relation has
3791  * within the inheritance hierarchy being processed, so we'll prohibit the
3792  * renaming only if there are additional parents from elsewhere.
3793  */
3794  if (attform->attinhcount > expected_parents)
3795  ereport(ERROR,
3796  (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
3797  errmsg("cannot rename inherited column \"%s\"",
3798  oldattname)));
3799 
3800  /* new name should not already exist */
3801  (void) check_for_column_name_collision(targetrelation, newattname, false);
3802 
3803  /* apply the update */
3804  namestrcpy(&(attform->attname), newattname);
3805 
3806  CatalogTupleUpdate(attrelation, &atttup->t_self, atttup);
3807 
3808  InvokeObjectPostAlterHook(RelationRelationId, myrelid, attnum);
3809 
3810  heap_freetuple(atttup);
3811 
3812  table_close(attrelation, RowExclusiveLock);
3813 
3814  relation_close(targetrelation, NoLock); /* close rel but keep lock */
3815 
3816  return attnum;
3817 }
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 3987 of file tablecmds.c.

3988 {
3989  Oid relid = InvalidOid;
3990  Oid typid = InvalidOid;
3991 
3992  if (stmt->renameType == OBJECT_DOMCONSTRAINT)
3993  {
3994  Relation rel;
3995  HeapTuple tup;
3996 
3997  typid = typenameTypeId(NULL, makeTypeNameFromNameList(castNode(List, stmt->object)));
3998  rel = table_open(TypeRelationId, RowExclusiveLock);
3999  tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
4000  if (!HeapTupleIsValid(tup))
4001  elog(ERROR, "cache lookup failed for type %u", typid);
4002  checkDomainOwner(tup);
4003  ReleaseSysCache(tup);
4004  table_close(rel, NoLock);
4005  }
4006  else
4007  {
4008  /* lock level taken here should match rename_constraint_internal */
4010  stmt->missing_ok ? RVR_MISSING_OK : 0,
4012  NULL);
4013  if (!OidIsValid(relid))
4014  {
4015  ereport(NOTICE,
4016  (errmsg("relation \"%s\" does not exist, skipping",
4017  stmt->relation->relname)));
4018  return InvalidObjectAddress;
4019  }
4020  }
4021 
4022  return
4023  rename_constraint_internal(relid, typid,
4024  stmt->subname,
4025  stmt->newname,
4026  (stmt->relation &&
4027  stmt->relation->inh), /* recursive? */
4028  false, /* recursing? */
4029  0 /* expected inhcount */ );
4030 }
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 4037 of file tablecmds.c.

4038 {
4039  bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4040  Oid relid;
4041  ObjectAddress address;
4042 
4043  /*
4044  * Grab an exclusive lock on the target table, index, sequence, view,
4045  * materialized view, or foreign table, which we will NOT release until
4046  * end of transaction.
4047  *
4048  * Lock level used here should match RenameRelationInternal, to avoid lock
4049  * escalation. However, because ALTER INDEX can be used with any relation
4050  * type, we mustn't believe without verification.
4051  */
4052  for (;;)
4053  {
4054  LOCKMODE lockmode;
4055  char relkind;
4056  bool obj_is_index;
4057 
4058  lockmode = is_index_stmt ? ShareUpdateExclusiveLock : AccessExclusiveLock;
4059 
4060  relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4061  stmt->missing_ok ? RVR_MISSING_OK : 0,
4063  (void *) stmt);
4064 
4065  if (!OidIsValid(relid))
4066  {
4067  ereport(NOTICE,
4068  (errmsg("relation \"%s\" does not exist, skipping",
4069  stmt->relation->relname)));
4070  return InvalidObjectAddress;
4071  }
4072 
4073  /*
4074  * We allow mismatched statement and object types (e.g., ALTER INDEX
4075  * to rename a table), but we might've used the wrong lock level. If
4076  * that happens, retry with the correct lock level. We don't bother
4077  * if we already acquired AccessExclusiveLock with an index, however.
4078  */
4079  relkind = get_rel_relkind(relid);
4080  obj_is_index = (relkind == RELKIND_INDEX ||
4081  relkind == RELKIND_PARTITIONED_INDEX);
4082  if (obj_is_index || is_index_stmt == obj_is_index)
4083  break;
4084 
4085  UnlockRelationOid(relid, lockmode);
4086  is_index_stmt = obj_is_index;
4087  }
4088 
4089  /* Do the work */
4090  RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4091 
4092  ObjectAddressSet(address, RelationRelationId, relid);
4093 
4094  return address;
4095 }

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

4102 {
4103  Relation targetrelation;
4104  Relation relrelation; /* for RELATION relation */
4105  HeapTuple reltup;
4106  Form_pg_class relform;
4107  Oid namespaceId;
4108 
4109  /*
4110  * Grab a lock on the target relation, which we will NOT release until end
4111  * of transaction. We need at least a self-exclusive lock so that
4112  * concurrent DDL doesn't overwrite the rename if they start updating
4113  * while still seeing the old version. The lock also guards against
4114  * triggering relcache reloads in concurrent sessions, which might not
4115  * handle this information changing under them. For indexes, we can use a
4116  * reduced lock level because RelationReloadIndexInfo() handles indexes
4117  * specially.
4118  */
4119  targetrelation = relation_open(myrelid, is_index ? ShareUpdateExclusiveLock : AccessExclusiveLock);
4120  namespaceId = RelationGetNamespace(targetrelation);
4121 
4122  /*
4123  * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4124  */
4125  relrelation = table_open(RelationRelationId, RowExclusiveLock);
4126 
4127  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4128  if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4129  elog(ERROR, "cache lookup failed for relation %u", myrelid);
4130  relform = (Form_pg_class) GETSTRUCT(reltup);
4131 
4132  if (get_relname_relid(newrelname, namespaceId) != InvalidOid)
4133  ereport(ERROR,
4134  (errcode(ERRCODE_DUPLICATE_TABLE),
4135  errmsg("relation \"%s\" already exists",
4136  newrelname)));
4137 
4138  /*
4139  * RenameRelation is careful not to believe the caller's idea of the
4140  * relation kind being handled. We don't have to worry about this, but
4141  * let's not be totally oblivious to it. We can process an index as
4142  * not-an-index, but not the other way around.
4143  */
4144  Assert(!is_index ||
4145  is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4146  targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4147 
4148  /*
4149  * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4150  * because it's a copy...)
4151  */
4152  namestrcpy(&(relform->relname), newrelname);
4153 
4154  CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4155 
4156  InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0,
4157  InvalidOid, is_internal);
4158 
4159  heap_freetuple(reltup);
4160  table_close(relrelation, RowExclusiveLock);
4161 
4162  /*
4163  * Also rename the associated type, if any.
4164  */
4165  if (OidIsValid(targetrelation->rd_rel->reltype))
4166  RenameTypeInternal(targetrelation->rd_rel->reltype,
4167  newrelname, namespaceId);
4168 
4169  /*
4170  * Also rename the associated constraint, if any.
4171  */
4172  if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4173  targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4174  {
4175  Oid constraintId = get_index_constraint(myrelid);
4176 
4177  if (OidIsValid(constraintId))
4178  RenameConstraintById(constraintId, newrelname);
4179  }
4180 
4181  /*
4182  * Close rel, but keep lock!
4183  */
4184  relation_close(targetrelation, NoLock);
4185 }
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 4191 of file tablecmds.c.

4192 {
4193  Relation relrelation; /* for RELATION relation */
4194  HeapTuple reltup;
4195  Form_pg_class relform;
4196 
4197  /*
4198  * Find relation's pg_class tuple.
4199  */
4200  relrelation = table_open(RelationRelationId, RowExclusiveLock);
4201 
4202  reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid));
4203  if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4204  elog(ERROR, "cache lookup failed for relation %u", myrelid);
4205  relform = (Form_pg_class) GETSTRUCT(reltup);
4206 
4207  /*
4208  * Update pg_class tuple.
4209  */
4210  relform->relrewrite = InvalidOid;
4211 
4212  CatalogTupleUpdate(relrelation, &reltup->t_self, reltup);
4213 
4214  heap_freetuple(reltup);
4215  table_close(relrelation, RowExclusiveLock);
4216 }

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

◆ SetIndexStorageProperties()

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

Definition at line 8799 of file tablecmds.c.

8804 {
8805  ListCell *lc;
8806 
8807  foreach(lc, RelationGetIndexList(rel))
8808  {
8809  Oid indexoid = lfirst_oid(lc);
8810  Relation indrel;
8811  AttrNumber indattnum = 0;
8812  HeapTuple tuple;
8813 
8814  indrel = index_open(indexoid, lockmode);
8815 
8816  for (int i = 0; i < indrel->rd_index->indnatts; i++)
8817  {
8818  if (indrel->rd_index->indkey.values[i] == attnum)
8819  {
8820  indattnum = i + 1;
8821  break;
8822  }
8823  }
8824 
8825  if (indattnum == 0)
8826  {
8827  index_close(indrel, lockmode);
8828  continue;
8829  }
8830 
8831  tuple = SearchSysCacheCopyAttNum(RelationGetRelid(indrel), indattnum);
8832 
8833  if (HeapTupleIsValid(tuple))
8834  {
8835  Form_pg_attribute attrtuple = (Form_pg_attribute) GETSTRUCT(tuple);
8836 
8837  if (setstorage)
8838  attrtuple->attstorage = newstorage;
8839 
8840  if (setcompression)
8841  attrtuple->attcompression = newcompression;
8842 
8843  CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8844 
8845  InvokeObjectPostAlterHook(RelationRelationId,
8846  RelationGetRelid(rel),
8847  attrtuple->attnum);
8848 
8849  heap_freetuple(tuple);
8850  }
8851 
8852  index_close(indrel, lockmode);
8853  }
8854 }
HeapTuple SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
Definition: syscache.c:445

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

3485 {
3486  Relation relationRelation;
3487  HeapTuple tuple;
3488  Form_pg_class classtuple;
3489 
3491  ShareUpdateExclusiveLock, false) ||
3492  CheckRelationOidLockedByMe(relationId,
3493  ShareRowExclusiveLock, true));
3494 
3495  /*
3496  * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3497  */
3498  relationRelation = table_open(RelationRelationId, RowExclusiveLock);
3499  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId));
3500  if (!HeapTupleIsValid(tuple))
3501  elog(ERROR, "cache lookup failed for relation %u", relationId);
3502  classtuple = (Form_pg_class) GETSTRUCT(tuple);
3503 
3504  if (classtuple->relhassubclass != relhassubclass)
3505  {
3506  classtuple->relhassubclass = relhassubclass;
3507  CatalogTupleUpdate(relationRelation, &tuple->t_self, tuple);
3508  }
3509  else
3510  {
3511  /* no need to change tuple, but force relcache rebuild anyway */
3513  }
3514 
3515  heap_freetuple(tuple);
3516  table_close(relationRelation, RowExclusiveLock);
3517 }
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition: inval.c:1396
bool CheckRelationOidLockedByMe(Oid relid, LOCKMODE lockmode, bool orstronger)
Definition: lmgr.c:347

References Assert, CacheInvalidateRelcacheByTuple(), CatalogTupleUpdate(), CheckRelationOidLockedByMe(), elog, ERROR, GETSTRUCT, heap_freetuple(), HeapTupleIsValid, ObjectIdGetDatum(), RowExclusiveLock, SearchSysCacheCopy1, ShareRowExclusiveLock, ShareUpdateExclusiveLock, HeapTupleData::t_self, table_close(), and table_open().

Referenced by acquire_inherited_sample_rows(), index_create(), IndexSetParentIndex(), and StoreCatalogInheritance1().

◆ SetRelationTableSpace()

void SetRelationTableSpace ( Relation  rel,
Oid  newTableSpaceId,
RelFileNumber  newRelFilenumber 
)

Definition at line 3587 of file tablecmds.c.

3590 {
3591  Relation pg_class;
3592  HeapTuple tuple;
3593  Form_pg_class rd_rel;
3594  Oid reloid = RelationGetRelid(rel);
3595 
3596  Assert(CheckRelationTableSpaceMove(rel, newTableSpaceId));
3597 
3598  /* Get a modifiable copy of the relation's pg_class row. */
3599  pg_class = table_open(RelationRelationId, RowExclusiveLock);
3600 
3601  tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(reloid));
3602  if (!HeapTupleIsValid(tuple))
3603  elog(ERROR, "cache lookup failed for relation %u", reloid);
3604  rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3605 
3606  /* Update the pg_class row. */
3607  rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3608  InvalidOid : newTableSpaceId;
3609  if (RelFileNumberIsValid(newRelFilenumber))
3610  rd_rel->relfilenode = newRelFilenumber;
3611  CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
3612 
3613  /*
3614  * Record dependency on tablespace. This is only required for relations
3615  * that have no physical storage.
3616  */
3617  if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3618  changeDependencyOnTablespace(RelationRelationId, reloid,
3619  rd_rel->reltablespace);
3620 
3621  heap_freetuple(tuple);
3622  table_close(pg_class, RowExclusiveLock);
3623 }
void changeDependencyOnTablespace(Oid classId, Oid objectId, Oid newTablespaceId)
Definition: pg_shdepend.c:391

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

2361 {
2362  switch (c)
2363  {
2364  case TYPSTORAGE_PLAIN:
2365  return "PLAIN";
2366  case TYPSTORAGE_EXTERNAL:
2367  return "EXTERNAL";
2368  case TYPSTORAGE_EXTENDED:
2369  return "EXTENDED";
2370  case TYPSTORAGE_MAIN:
2371  return "MAIN";
2372  default:
2373  return "???";
2374  }
2375 }
char * c

Referenced by MergeChildAttribute(), and MergeInheritedAttribute().

◆ StoreCatalogInheritance()

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

Definition at line 3358 of file tablecmds.c.

3360 {
3361  Relation relation;
3362  int32 seqNumber;
3363  ListCell *entry;
3364 
3365  /*
3366  * sanity checks
3367  */
3368  Assert(OidIsValid(relationId));
3369 
3370  if (supers == NIL)
3371  return;
3372 
3373  /*
3374  * Store INHERITS information in pg_inherits using direct ancestors only.
3375  * Also enter dependencies on the direct ancestors, and make sure they are
3376  * marked with relhassubclass = true.
3377  *
3378  * (Once upon a time, both direct and indirect ancestors were found here
3379  * and then entered into pg_ipl. Since that catalog doesn't exist
3380  * anymore, there's no need to look for indirect ancestors.)
3381  */
3382  relation = table_open(InheritsRelationId, RowExclusiveLock);
3383 
3384  seqNumber = 1;
3385  foreach(entry, supers)
3386  {
3387  Oid parentOid = lfirst_oid(entry);
3388 
3389  StoreCatalogInheritance1(relationId, parentOid, seqNumber, relation,
3390  child_is_partition);
3391  seqNumber++;
3392  }
3393 
3394  table_close(relation, RowExclusiveLock);
3395 }

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

3405 {
3406  ObjectAddress childobject,
3407  parentobject;
3408 
3409  /* store the pg_inherits row */
3410  StoreSingleInheritance(relationId, parentOid, seqNumber);
3411 
3412  /*
3413  * Store a dependency too
3414  */
3415  parentobject.classId = RelationRelationId;
3416  parentobject.objectId = parentOid;
3417  parentobject.objectSubId = 0;
3418  childobject.classId = RelationRelationId;
3419  childobject.objectId = relationId;
3420  childobject.objectSubId = 0;
3421 
3422  recordDependencyOn(&childobject, &parentobject,
3423  child_dependency_type(child_is_partition));
3424 
3425  /*
3426  * Post creation hook of this inheritance. Since object_access_hook
3427  * doesn't take multiple object identifiers, we relay oid of parent
3428  * relation using auxiliary_id argument.
3429  */
3430  InvokeObjectPostAlterHookArg(InheritsRelationId,
3431  relationId, 0,
3432  parentOid, false);
3433 
3434  /*
3435  * Mark the parent as having subclasses.
3436  */
3437  SetRelationHasSubclass(parentOid, true);
3438 }
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
Definition: pg_inherits.c:508
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition: tablecmds.c:3484

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

11767 {
11768  ListCell *l;
11769  int attnum;
11770 
11771  attnum = 0;
11772  foreach(l, colList)
11773  {
11774  char *attname = strVal(lfirst(l));
11775  HeapTuple atttuple;
11776  Form_pg_attribute attform;
11777 
11778  atttuple = SearchSysCacheAttName(relId, attname);
11779  if (!HeapTupleIsValid(atttuple))
11780  ereport(ERROR,
11781  (errcode(ERRCODE_UNDEFINED_COLUMN),
11782  errmsg("column \"%s\" referenced in foreign key constraint does not exist",
11783  attname)));
11784  attform = (Form_pg_attribute) GETSTRUCT(atttuple);
11785  if (attform->attnum < 0)
11786  ereport(ERROR,
11787  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
11788  errmsg("system columns cannot be used in foreign keys")));
11789  if (attnum >= INDEX_MAX_KEYS)
11790  ereport(ERROR,
11791  (errcode(ERRCODE_TOO_MANY_COLUMNS),
11792  errmsg("cannot have more than %d keys in a foreign key",
11793  INDEX_MAX_KEYS)));
11794  attnums[attnum] = attform->attnum;
11795  if (atttypids != NULL)
11796  atttypids[attnum] = attform->atttypid;
11797  ReleaseSysCache(atttuple);
11798  attnum++;
11799  }
11800 
11801  return attnum;
11802 }

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

Definition at line 11916 of file tablecmds.c.

11919 {
11920  Oid indexoid = InvalidOid;
11921  bool found = false;
11922  bool found_deferrable = false;
11923  List *indexoidlist;
11924  ListCell *indexoidscan;
11925  int i,
11926  j;
11927 
11928  /*
11929  * Reject duplicate appearances of columns in the referenced-columns list.
11930  * Such a case is forbidden by the SQL standard, and even if we thought it
11931  * useful to allow it, there would be ambiguity about how to match the
11932  * list to unique indexes (in particular, it'd be unclear which index
11933  * opclass goes with which FK column).
11934  */
11935  for (i = 0; i < numattrs; i++)
11936  {
11937  for (j = i + 1; j < numattrs; j++)
11938  {
11939  if (attnums[i] == attnums[j])
11940  ereport(ERROR,
11941  (errcode(ERRCODE_INVALID_FOREIGN_KEY),
11942  errmsg("foreign key referenced-columns list must not contain duplicates")));
11943  }
11944  }
11945 
11946  /*
11947  * Get the list of index OIDs for the table from the relcache, and look up
11948  * each one in the pg_index syscache, and match unique indexes to the list
11949  * of attnums we are given.
11950  */
11951  indexoidlist = RelationGetIndexList(pkrel);
11952 
11953  foreach(indexoidscan, indexoidlist)
11954  {
11955  HeapTuple indexTuple;
11956  Form_pg_index indexStruct;
11957 
11958  indexoid = lfirst_oid(indexoidscan);
11959  indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
11960  if (!HeapTupleIsValid(indexTuple))
11961  elog(ERROR, "cache lookup failed for index %u", indexoid);
11962  indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
11963 
11964  /*
11965  * Must have the right number of columns; must be unique and not a
11966  * partial index; forget it if there are any expressions, too. Invalid
11967  * indexes are out as well.
11968  */
11969  if (indexStruct->indnkeyatts == numattrs &&
11970  indexStruct->indisunique &&
11971  indexStruct->indisvalid &&
11972  heap_attisnull(indexTuple, Anum_pg_index_indpred, NULL) &&
11973  heap_attisnull(indexTuple, Anum_pg_index_indexprs, NULL))
11974  {
11975  Datum indclassDatum;
11976  oidvector *indclass;
11977 
11978  /* Must get indclass the hard way */
11979  indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11980  Anum_pg_index_indclass);
11981  indclass = (oidvector *) DatumGetPointer(indclassDatum);
11982 
11983  /*
11984  * The given attnum list may match the index columns in any order.
11985  * Check for a match, and extract the appropriate opclasses while
11986  * we're at it.
11987  *
11988  * We know that attnums[] is duplicate-free per the test at the
11989  * start of this function, and we checked above that the number of
11990  * index columns agrees, so if we find a match for each attnums[]
11991  * entry then we must have a one-to-one match in some order.
11992  */
11993  for (i = 0; i < numattrs; i++)
11994  {
11995  found = false;
11996  for (j = 0; j < numattrs; j++)
11997  {
11998  if (attnums[i] == indexStruct->indkey.values[j])
11999  {
12000  opclasses[i] = indclass->values[j];
12001  found = true;
12002  break;
12003  }
12004  }
12005  if (!found)
12006  break;
12007  }
12008 
12009  /*
12010  * Refuse to use a deferrable unique/primary key. This is per SQL
12011  * spec, and there would be a lot of interesting semantic problems
12012  * if we tried to allow it.
12013  */
12014  if (found && !indexStruct->indimmediate)
12015  {
12016  /*
12017  * Remember that we found an otherwise matching index, so that
12018  * we can generate a more appropriate error message.
12019  */
12020  found_deferrable = true;
12021  found = false;
12022  }
12023  }
12024  ReleaseSysCache(indexTuple);
12025  if (found)
12026  break;
12027  }
12028 
12029  if (!found)
12030  {
12031  if (found_deferrable)
12032  ereport(ERROR,
12033  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
12034  errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
12035  RelationGetRelationName(pkrel))));
12036  else
12037  ereport(ERROR,
12038  (errcode(ERRCODE_INVALID_FOREIGN_KEY),
12039  errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
12040  RelationGetRelationName(pkrel))));
12041  }
12042 
12043  list_free(indexoidlist);
12044 
12045  return indexoid;
12046 }
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 
)
static

Definition at line 11817 of file tablecmds.c.

11821 {
11822  List *indexoidlist;
11823  ListCell *indexoidscan;
11824  HeapTuple indexTuple = NULL;
11825  Form_pg_index indexStruct = NULL;
11826  Datum indclassDatum;
11827  oidvector *indclass;
11828  int i;
11829 
11830  /*
11831  * Get the list of index OIDs for the table from the relcache, and look up
11832  * each one in the pg_index syscache until we find one marked primary key
11833  * (hopefully there isn't more than one such). Insist it's valid, too.
11834  */
11835  *indexOid = InvalidOid;
11836 
11837  indexoidlist = RelationGetIndexList(pkrel);
11838 
11839  foreach(indexoidscan, indexoidlist)
11840  {
11841  Oid indexoid = lfirst_oid(indexoidscan);
11842 
11843  indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
11844  if (!HeapTupleIsValid(indexTuple))
11845  elog(ERROR, "cache lookup failed for index %u", indexoid);
11846  indexStruct = (Form_pg_index) GETSTRUCT(indexTuple);
11847  if (indexStruct->indisprimary && indexStruct->indisvalid)
11848  {
11849  /*
11850  * Refuse to use a deferrable primary key. This is per SQL spec,
11851  * and there would be a lot of interesting semantic problems if we
11852  * tried to allow it.
11853  */
11854  if (!indexStruct->indimmediate)
11855  ereport(ERROR,
11856  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
11857  errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
11858  RelationGetRelationName(pkrel))));
11859 
11860  *indexOid = indexoid;
11861  break;
11862  }
11863  ReleaseSysCache(indexTuple);
11864  }
11865 
11866  list_free(indexoidlist);
11867 
11868  /*
11869  * Check that we found it
11870  */
11871  if (!OidIsValid(*indexOid))
11872  ereport(ERROR,
11873  (errcode(ERRCODE_UNDEFINED_OBJECT),
11874  errmsg("there is no primary key for referenced table \"%s\"",
11875  RelationGetRelationName(pkrel))));
11876 
11877  /* Must get indclass the hard way */
11878  indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, indexTuple,
11879  Anum_pg_index_indclass);
11880  indclass = (oidvector *) DatumGetPointer(indclassDatum);
11881 
11882  /*
11883  * Now build the list of PK attributes from the indkey definition (we
11884  * assume a primary key cannot have expressional elements)
11885  */
11886  *attnamelist = NIL;
11887  for (i = 0; i < indexStruct->indnkeyatts; i++)
11888  {
11889  int pkattno = indexStruct->indkey.values[i];
11890 
11891  attnums[i] = pkattno;
11892  atttypids[i] = attnumTypeId(pkrel, pkattno);
11893  opclasses[i] = indclass->values[i];
11894  *attnamelist = lappend(*attnamelist,
11895  makeString(pstrdup(NameStr(*attnumAttName(pkrel, pkattno)))));
11896  }
11897 
11898  ReleaseSysCache(indexTuple);
11899 
11900  return i;
11901 }
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 17818 of file tablecmds.c.

17819 {
17820  PartitionSpec *newspec;
17821  ParseState *pstate;
17822  ParseNamespaceItem *nsitem;
17823  ListCell *l;
17824 
17825  newspec = makeNode(PartitionSpec);
17826 
17827  newspec->strategy = partspec->strategy;
17828  newspec->partParams = NIL;
17829  newspec->location = partspec->location;
17830 
17831  /* Check valid number of columns for strategy */
17832  if (partspec->strategy == PARTITION_STRATEGY_LIST &&
17833  list_length(partspec->partParams) != 1)
17834  ereport(ERROR,
17835  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
17836  errmsg("cannot use \"list\" partition strategy with more than one column")));
17837 
17838  /*
17839  * Create a dummy ParseState and insert the target relation as its sole
17840  * rangetable entry. We need a ParseState for transformExpr.
17841  */
17842  pstate = make_parsestate(NULL);
17843  nsitem = addRangeTableEntryForRelation(pstate, rel, AccessShareLock,
17844  NULL, false, true);
17845  addNSItemToQuery(pstate, nsitem, true, true, true);
17846 
17847  /* take care of any partition expressions */
17848  foreach(l, partspec->partParams)
17849  {
17851 
17852  if (pelem->expr)
17853  {
17854  /* Copy, to avoid scribbling on the input */
17855  pelem = copyObject(pelem);
17856 
17857  /* Now do parse transformation of the expression */
17858  pelem->expr = transformExpr(pstate, pelem->expr,
17860 
17861  /* we have to fix its collations too */
17862  assign_expr_collations(pstate, pelem->expr);
17863  }
17864 
17865  newspec->partParams = lappend(newspec->partParams, pelem);
17866  }
17867 
17868  return newspec;
17869 }
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:120
@ 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 2337 of file tablecmds.c.

2338 {
2339  /*
2340  * Don't allow truncate on temp tables of other backends ... their local
2341  * buffer manager is not going to cope.
2342  */
2343  if (RELATION_IS_OTHER_TEMP(rel))
2344  ereport(ERROR,
2345  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2346  errmsg("cannot truncate temporary tables of other sessions")));
2347 
2348  /*
2349  * Also check for active uses of the relation in the current transaction,
2350  * including open scans and pending AFTER trigger events.
2351  */
2352  CheckTableNotInUse(rel, "TRUNCATE");
2353 }

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

2320 {
2321  char *relname = NameStr(reltuple->relname);
2322  AclResult aclresult;
2323 
2324  /* Permissions checks */
2325  aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_TRUNCATE);
2326  if (aclresult != ACLCHECK_OK)
2327  aclcheck_error(aclresult, get_relkind_objtype(reltuple->relkind),
2328  relname);
2329 }
#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 2271 of file tablecmds.c.

2272 {
2273  char *relname = NameStr(reltuple->relname);
2274 
2275  /*
2276  * Only allow truncate on regular tables, foreign tables using foreign
2277  * data wrappers supporting TRUNCATE and partitioned tables (although, the
2278  * latter are only being included here for the following checks; no
2279  * physical truncation will occur in their case.).
2280  */
2281  if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2282  {
2283  Oid serverid = GetForeignServerIdByRelId(relid);
2284  FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2285 
2286  if (!fdwroutine->ExecForeignTruncate)
2287  ereport(ERROR,
2288  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2289  errmsg("cannot truncate foreign table \"%s\"",
2290  relname)));
2291  }
2292  else if (reltuple->relkind != RELKIND_RELATION &&
2293  reltuple->relkind != RELKIND_PARTITIONED_TABLE)
2294  ereport(ERROR,
2295  (errcode(ERRCODE_WRONG_OBJECT_TYPE),
2296  errmsg("\"%s\" is not a table", relname)));
2297 
2298  /*
2299  * Most system catalogs can't be truncated at all, or at least not unless
2300  * allow_system_table_mods=on. As an exception, however, we allow
2301  * pg_largeobject to be truncated as part of pg_upgrade, because we need
2302  * to change its relfilenode to match the old cluster, and allowing a
2303  * TRUNCATE command to be executed is the easiest way of doing that.
2304  */
2305  if (!allowSystemTableMods && IsSystemClass(relid, reltuple)
2306  && (!IsBinaryUpgrade || relid != LargeObjectRelationId))
2307  ereport(ERROR,
2308  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2309  errmsg("permission denied: \"%s\" is a system catalog",
2310  relname)));
2311 
2312  InvokeObjectTruncateHook(relid);
2313 }
bool IsBinaryUpgrade
Definition: globals.c:119
#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 11017 of file tablecmds.c.

11027 {
11028  HeapTuple parentConstrTup;
11029  Form_pg_constraint parentConstr;
11030  HeapTuple partcontup;
11031  Form_pg_constraint partConstr;
11032  ScanKeyData key;
11033  SysScanDesc scan;
11034  HeapTuple trigtup;
11035  Oid insertTriggerOid,
11036  updateTriggerOid;
11037 
11038  parentConstrTup = SearchSysCache1(CONSTROID,
11039  ObjectIdGetDatum(parentConstrOid));
11040  if (!HeapTupleIsValid(parentConstrTup))
11041  elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11042  parentConstr = (Form_pg_constraint) GETSTRUCT(parentConstrTup);
11043 
11044  /*
11045  * Do some quick & easy initial checks. If any of these fail, we cannot
11046  * use this constraint.
11047  */
11048  if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11049  {
11050  ReleaseSysCache(parentConstrTup);
11051  return false;
11052  }
11053  for (int i = 0; i < numfks; i++)
11054  {
11055  if (fk->conkey[i] != mapped_conkey[i] ||
11056  fk->confkey[i] != confkey[i] ||
11057  fk->conpfeqop[i] != conpfeqop[i])
11058  {
11059  ReleaseSysCache(parentConstrTup);
11060  return false;
11061  }
11062  }
11063 
11064  /*
11065  * Looks good so far; do some more extensive checks. Presumably the check
11066  * for 'convalidated' could be dropped, since we don't really care about
11067  * that, but let's be careful for now.
11068  */
11069  partcontup = SearchSysCache1(CONSTROID,
11070  ObjectIdGetDatum(fk->conoid));
11071  if (!HeapTupleIsValid(partcontup))
11072  elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11073  partConstr = (Form_pg_constraint) GETSTRUCT(partcontup);
11074  if (OidIsValid(partConstr->conparentid) ||
11075  !partConstr->convalidated ||
11076  partConstr->condeferrable != parentConstr->condeferrable ||
11077  partConstr->condeferred != parentConstr->condeferred ||
11078  partConstr->confupdtype != parentConstr->confupdtype ||
11079  partConstr->confdeltype != parentConstr->confdeltype ||
11080  partConstr->confmatchtype != parentConstr->confmatchtype)
11081  {
11082  ReleaseSysCache(parentConstrTup);
11083  ReleaseSysCache(partcontup);
11084  return false;
11085  }
11086 
11087  ReleaseSysCache(partcontup);
11088  ReleaseSysCache(parentConstrTup);
11089 
11090  /*
11091  * Looks good! Attach this constraint. The action triggers in the new
11092  * partition become redundant -- the parent table already has equivalent
11093  * ones, and those will be able to reach the partition. Remove the ones
11094  * in the partition. We identify them because they have our constraint
11095  * OID, as well as being on the referenced rel.
11096  */
11097  ScanKeyInit(&key,
11098  Anum_pg_trigger_tgconstraint,
11099  BTEqualStrategyNumber, F_OIDEQ,
11100  ObjectIdGetDatum(fk->conoid));
11101  scan = systable_beginscan(trigrel, TriggerConstraintIndexId, true,
11102  NULL, 1, &key);
11103  while ((trigtup = systable_getnext(scan)) != NULL)
11104  {
11105  Form_pg_trigger trgform = (Form_pg_trigger) GETSTRUCT(trigtup);
11106  ObjectAddress trigger;
11107 
11108  if (trgform->tgconstrrelid != fk->conrelid)
11109  continue;
11110  if (trgform->tgrelid != fk->confrelid)
11111  continue;
11112 
11113  /*
11114  * The constraint is originally set up to contain this trigger as an
11115  * implementation object, so there's a dependency record that links
11116  * the two; however, since the trigger is no longer needed, we remove
11117  * the dependency link in order to be able to drop the trigger while
11118  * keeping the constraint intact.
11119  */
11120  deleteDependencyRecordsFor(TriggerRelationId,
11121  trgform->oid,
11122  false);
11123  /* make dependency deletion visible to performDeletion */
11125  ObjectAddressSet(trigger, TriggerRelationId,
11126  trgform->oid);
11127  performDeletion(&trigger, DROP_RESTRICT, 0);
11128  /* make trigger drop visible, in case the loop iterates */
11130  }
11131 
11132  systable_endscan(scan);
11133 
11134  ConstraintSetParentConstraint(fk->conoid, parentConstrOid, partRelid);
11135 
11136  /*
11137  * Like the constraint, attach partition's "check" triggers to the
11138  * corresponding parent triggers.
11139  */
11141  fk->conoid, fk->confrelid, fk->conrelid,
11142  &insertTriggerOid, &updateTriggerOid);
11143  Assert(OidIsValid(insertTriggerOid) && OidIsValid(parentInsTrigger));
11144  TriggerSetParentTrigger(trigrel, insertTriggerOid, parentInsTrigger,
11145  partRelid);
11146  Assert(OidIsValid(updateTriggerOid) && OidIsValid(parentUpdTrigger));
11147  TriggerSetParentTrigger(trigrel, updateTriggerOid, parentUpdTrigger,
11148  partRelid);
11149 
11151  return true;
11152 }

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

14158 {
14159  HeapTuple tup;
14160  Datum adatum;
14161  ArrayType *arr;
14162  Oid *rawarr;
14163  int numkeys;
14164  int i;
14165 
14166  Assert(con->contype == CONSTR_FOREIGN);
14167  Assert(con->old_conpfeqop == NIL); /* already prepared this node */
14168 
14169  tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(oldId));
14170  if (!HeapTupleIsValid(tup)) /* should not happen */
14171  elog(ERROR, "cache lookup failed for constraint %u", oldId);
14172 
14173  adatum = SysCacheGetAttrNotNull(CONSTROID, tup,
14174  Anum_pg_constraint_conpfeqop);
14175  arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
14176  numkeys = ARR_DIMS(arr)[0];
14177  /* test follows the one in ri_FetchConstraintInfo() */
14178  if (ARR_NDIM(arr) != 1 ||
14179  ARR_HASNULL(arr) ||
14180  ARR_ELEMTYPE(arr) != OIDOID)
14181  elog(ERROR, "conpfeqop is not a 1-D Oid array");
14182  rawarr = (Oid *) ARR_DATA_PTR(arr);
14183 
14184  /* stash a List of the operator Oids in our Constraint node */
14185  for (i = 0; i < numkeys; i++)
14186  con->old_conpfeqop = lappend_oid(con->old_conpfeqop, rawarr[i]);
14187 
14188  ReleaseSysCache(tup);
14189 }
#define ARR_NDIM(a)
Definition: array.h:290
#define ARR_DATA_PTR(a)
Definition: array.h:322
#define DatumGetArrayTypeP(X)
Definition: array.h:261
#define ARR_ELEMTYPE(a)
Definition: array.h:292
#define ARR_DIMS(a)
Definition: array.h:294
#define ARR_HASNULL(a)
Definition: array.h:291

References ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_HASNULL, ARR_NDIM, Assert, CONSTR_FOREIGN, Constraint::contype, DatumGetArrayTypeP, elog, ERROR, HeapTupleIsValid, i, lappend_oid(), NIL, ObjectIdGetDatum(), Constraint::old_conpfeqop, ReleaseSysCache(), SearchSysCache1(), and SysCacheGetAttrNotNull().

Referenced by ATPostAlterTypeParse().

◆ TryReuseIndex()

static void TryReuseIndex ( Oid  oldId,
IndexStmt stmt 
)
static

Definition at line 14129 of file tablecmds.c.

14130 {
14131  if (CheckIndexCompatible(oldId,
14132  stmt->accessMethod,
14133  stmt->indexParams,
14134  stmt->excludeOpNames))
14135  {
14136  Relation irel = index_open(oldId, NoLock);
14137 
14138  /* If it's a partitioned index, there is no storage to share. */
14139  if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
14140  {
14141  stmt->oldNumber = irel->rd_locator.relNumber;
14142  stmt->oldCreateSubid = irel->rd_createSubid;
14143  stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
14144  }
14145  index_close(irel, NoLock);
14146  }
14147 }
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames)
Definition: indexcmds.c:174

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

10010 {
10011  for (int i = 0; i < numfksetcols; i++)
10012  {
10013  int16 setcol_attnum = fksetcolsattnums[i];
10014  bool seen = false;
10015 
10016  for (int j = 0; j < numfks; j++)
10017  {
10018  if (fkattnums[j] == setcol_attnum)
10019  {
10020  seen = true;
10021  break;
10022  }
10023  }
10024 
10025  if (!seen)
10026  {
10027  char *col = strVal(list_nth(fksetcols, i));
10028 
10029  ereport(ERROR,
10030  (errcode(ERRCODE_INVALID_COLUMN_REFERENCE),
10031  errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10032  }
10033  }
10034 }

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

Definition at line 12113 of file tablecmds.c.

12118 {
12119  TupleTableSlot *slot;
12120  TableScanDesc scan;
12121  Trigger trig = {0};
12122  Snapshot snapshot;
12123  MemoryContext oldcxt;
12124  MemoryContext perTupCxt;
12125 
12126  ereport(DEBUG1,
12127  (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
12128 
12129  /*
12130  * Build a trigger call structure; we'll need it either way.
12131  */
12132  trig.tgoid = InvalidOid;
12133  trig.tgname = conname;
12135  trig.tgisinternal = true;
12136  trig.tgconstrrelid = RelationGetRelid(pkrel);
12137  trig.tgconstrindid = pkindOid;
12138  trig.tgconstraint = constraintOid;
12139  trig.tgdeferrable = false;
12140  trig.tginitdeferred = false;
12141  /* we needn't fill in remaining fields */
12142 
12143  /*
12144  * See if we can do it with a single LEFT JOIN query. A false result
12145  * indicates we must proceed with the fire-the-trigger method.
12146  */
12147  if (RI_Initial_Check(&trig, rel, pkrel))
12148  return;
12149 
12150  /*
12151  * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
12152  * if that tuple had just been inserted. If any of those fail, it should
12153  * ereport(ERROR) and that's that.
12154  */
12155  snapshot = RegisterSnapshot(GetLatestSnapshot());
12156  slot = table_slot_create(rel, NULL);
12157  scan = table_beginscan(rel, snapshot, 0, NULL);
12158 
12160  "validateForeignKeyConstraint",
12162  oldcxt = MemoryContextSwitchTo(perTupCxt);
12163 
12164  while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
12165  {
12166  LOCAL_FCINFO(fcinfo, 0);
12167  TriggerData trigdata = {0};
12168 
12170 
12171  /*
12172  * Make a call to the trigger function
12173  *
12174  * No parameters are passed, but we do set a context
12175  */
12176  MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
12177 
12178  /*
12179  * We assume RI_FKey_check_ins won't look at flinfo...
12180  */
12181  trigdata.type = T_TriggerData;
12183  trigdata.tg_relation = rel;
12184  trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
12185  trigdata.tg_trigslot = slot;
12186  trigdata.tg_trigger = &trig;
12187 
12188  fcinfo->context = (Node *) &trigdata;
12189 
12190  RI_FKey_check_ins(fcinfo);
12191 
12192  MemoryContextReset(perTupCxt);
12193  }
12194 
12195  MemoryContextSwitchTo(oldcxt);
12196  MemoryContextDelete(perTupCxt);
12197  table_endscan(scan);
12198  UnregisterSnapshot(snapshot);
12200 }
#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:424
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
Definition: ri_triggers.c:1359
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 19787 of file tablecmds.c.

19788 {
19789  Relation inheritsRel;
19790  SysScanDesc scan;
19791  ScanKeyData key;
19792  int tuples = 0;
19793  HeapTuple inhTup;
19794  bool updated = false;
19795 
19796  Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
19797 
19798  /*
19799  * Scan pg_inherits for this parent index. Count each valid index we find
19800  * (verifying the pg_index entry for each), and if we reach the total
19801  * amount we expect, we can mark this parent index as valid.
19802  */
19803  inheritsRel = table_open(InheritsRelationId, AccessShareLock);
19804  ScanKeyInit(&key, Anum_pg_inherits_inhparent,
19805  BTEqualStrategyNumber, F_OIDEQ,
19806  ObjectIdGetDatum(RelationGetRelid(partedIdx)));
19807  scan = systable_beginscan(inheritsRel, InheritsParentIndexId, true,
19808  NULL, 1, &key);
19809  while ((inhTup = systable_getnext(scan)) != NULL)
19810  {
19811  Form_pg_inherits inhForm = (Form_pg_inherits) GETSTRUCT(inhTup);
19812  HeapTuple indTup;
19813  Form_pg_index indexForm;
19814 
19815  indTup = SearchSysCache1(INDEXRELID,
19816  ObjectIdGetDatum(inhForm->inhrelid));
19817  if (!HeapTupleIsValid(indTup))
19818  elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
19819  indexForm = (Form_pg_index) GETSTRUCT(indTup);
19820  if (indexForm->indisvalid)
19821  tuples += 1;
19822  ReleaseSysCache(indTup);
19823  }
19824 
19825  /* Done with pg_inherits */
19826  systable_endscan(scan);
19827  table_close(inheritsRel, AccessShareLock);
19828 
19829  /*
19830  * If we found as many inherited indexes as the partitioned table has
19831  * partitions, we're good; update pg_index to set indisvalid.
19832  */
19833  if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
19834  {
19835  Relation idxRel;
19836  HeapTuple indTup;
19837  Form_pg_index indexForm;
19838 
19839  idxRel = table_open(IndexRelationId, RowExclusiveLock);
19840  indTup = SearchSysCacheCopy1(INDEXRELID,
19841  ObjectIdGetDatum(RelationGetRelid(partedIdx)));
19842  if (!HeapTupleIsValid(indTup))
19843  elog(ERROR, "cache lookup failed for index %u",
19844  RelationGetRelid(partedIdx));
19845  indexForm = (Form_pg_index) GETSTRUCT(indTup);
19846 
19847  indexForm->indisvalid = true;
19848  updated = true;
19849 
19850  CatalogTupleUpdate(idxRel, &indTup->t_self, indTup);
19851 
19852  table_close(idxRel, RowExclusiveLock);
19853  heap_freetuple(indTup);
19854  }
19855 
19856  /*
19857  * If this index is in turn a partition of a larger index, validating it
19858  * might cause the parent to become valid also. Try that.
19859  */
19860  if (updated && partedIdx->rd_rel->relispartition)
19861  {
19862  Oid parentIdxId,
19863  parentTblId;
19864  Relation parentIdx,
19865  parentTbl;
19866 
19867  /* make sure we see the validation we just did */
19869 
19870  parentIdxId = get_partition_parent(RelationGetRelid(partedIdx), false);
19871  parentTblId = get_partition_parent(RelationGetRelid(partedTbl), false);
19872  parentIdx = relation_open(parentIdxId, AccessExclusiveLock);
19873  parentTbl = relation_open(parentTblId, AccessExclusiveLock);
19874  Assert(!parentIdx->rd_index->indisvalid);
19875 
19876  validatePartitionedIndex(parentIdx, parentTbl);
19877 
19878  relation_close(parentIdx, AccessExclusiveLock);
19879  relation_close(parentTbl, AccessExclusiveLock);
19880  }
19881 }

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

Variable Documentation

◆ dropmsgstringarray

const struct dropmsgstrings dropmsgstringarray[]
static

Definition at line 129 of file tablecmds.c.

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits