PostgreSQL Source Code git master
Loading...
Searching...
No Matches
tablecmds.c File Reference
#include "postgres.h"
#include "access/attmap.h"
#include "access/genam.h"
#include "access/gist.h"
#include "access/heapam.h"
#include "access/heapam_xlog.h"
#include "access/multixact.h"
#include "access/reloptions.h"
#include "access/relscan.h"
#include "access/sysattr.h"
#include "access/tableam.h"
#include "access/toast_compression.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "access/xloginsert.h"
#include "catalog/catalog.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/objectaccess.h"
#include "catalog/partition.h"
#include "catalog/pg_am.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication_rel.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "catalog/toasting.h"
#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "commands/user.h"
#include "commands/vacuum.h"
#include "common/int.h"
#include "executor/executor.h"
#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "parser/parse_utilcmd.h"
#include "parser/parser.h"
#include "partitioning/partbounds.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/lock.h"
#include "storage/predicate.h"
#include "storage/smgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/partcache.h"
#include "utils/relcache.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
#include "utils/usercontext.h"
Include dependency graph for tablecmds.c:

Go to the source code of this file.

Data Structures

struct  OnCommitItem
 
struct  AlteredTableInfo
 
struct  NewConstraint
 
struct  NewColumnValue
 
struct  dropmsgstrings
 
struct  DropRelationCallbackState
 
struct  ForeignTruncateInfo
 
struct  AttachIndexCallbackState
 
struct  SplitPartitionContext
 

Macros

#define AT_NUM_PASSES   (AT_PASS_MISC + 1)
 
#define ATT_TABLE   0x0001
 
#define ATT_VIEW   0x0002
 
#define ATT_MATVIEW   0x0004
 
#define ATT_INDEX   0x0008
 
#define ATT_COMPOSITE_TYPE   0x0010
 
#define ATT_FOREIGN_TABLE   0x0020
 
#define ATT_PARTITIONED_INDEX   0x0040
 
#define ATT_SEQUENCE   0x0080
 
#define ATT_PARTITIONED_TABLE   0x0100
 
#define child_dependency_type(child_is_partition)    ((child_is_partition) ? DEPENDENCY_AUTO : DEPENDENCY_NORMAL)
 

Typedefs

typedef struct OnCommitItem OnCommitItem
 
typedef enum AlterTablePass AlterTablePass
 
typedef struct AlteredTableInfo AlteredTableInfo
 
typedef struct NewConstraint NewConstraint
 
typedef struct NewColumnValue NewColumnValue
 
typedef struct ForeignTruncateInfo ForeignTruncateInfo
 
typedef enum addFkConstraintSides addFkConstraintSides
 
typedef struct SplitPartitionContext SplitPartitionContext
 

Enumerations

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

Functions

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

Variables

static Liston_commits = NIL
 
static const struct dropmsgstrings dropmsgstringarray []
 

Macro Definition Documentation

◆ AT_NUM_PASSES

#define AT_NUM_PASSES   (AT_PASS_MISC + 1)

Definition at line 167 of file tablecmds.c.

◆ ATT_COMPOSITE_TYPE

#define ATT_COMPOSITE_TYPE   0x0010

Definition at line 333 of file tablecmds.c.

◆ ATT_FOREIGN_TABLE

#define ATT_FOREIGN_TABLE   0x0020

Definition at line 334 of file tablecmds.c.

◆ ATT_INDEX

#define ATT_INDEX   0x0008

Definition at line 332 of file tablecmds.c.

◆ ATT_MATVIEW

#define ATT_MATVIEW   0x0004

Definition at line 331 of file tablecmds.c.

◆ ATT_PARTITIONED_INDEX

#define ATT_PARTITIONED_INDEX   0x0040

Definition at line 335 of file tablecmds.c.

◆ ATT_PARTITIONED_TABLE

#define ATT_PARTITIONED_TABLE   0x0100

Definition at line 337 of file tablecmds.c.

◆ ATT_SEQUENCE

#define ATT_SEQUENCE   0x0080

Definition at line 336 of file tablecmds.c.

◆ ATT_TABLE

#define ATT_TABLE   0x0001

Definition at line 329 of file tablecmds.c.

◆ ATT_VIEW

#define ATT_VIEW   0x0002

Definition at line 330 of file tablecmds.c.

◆ child_dependency_type

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

Definition at line 366 of file tablecmds.c.

@ DEPENDENCY_NORMAL
Definition dependency.h:33

Typedef Documentation

◆ addFkConstraintSides

◆ AlteredTableInfo

◆ AlterTablePass

◆ ForeignTruncateInfo

◆ NewColumnValue

◆ NewConstraint

◆ OnCommitItem

◆ SplitPartitionContext

Enumeration Type Documentation

◆ addFkConstraintSides

Enumerator
addFkReferencedSide 
addFkReferencingSide 
addFkBothSides 

Definition at line 354 of file tablecmds.c.

355{
addFkConstraintSides
Definition tablecmds.c:355
@ addFkReferencingSide
Definition tablecmds.c:357
@ addFkBothSides
Definition tablecmds.c:358
@ addFkReferencedSide
Definition tablecmds.c:356

◆ AlterTablePass

Enumerator
AT_PASS_UNSET 
AT_PASS_DROP 
AT_PASS_ALTER_TYPE 
AT_PASS_ADD_COL 
AT_PASS_SET_EXPRESSION 
AT_PASS_OLD_INDEX 
AT_PASS_OLD_CONSTR 
AT_PASS_ADD_CONSTR 
AT_PASS_COL_ATTRS 
AT_PASS_ADD_INDEXCONSTR 
AT_PASS_ADD_INDEX 
AT_PASS_ADD_OTHERCONSTR 
AT_PASS_MISC 

Definition at line 149 of file tablecmds.c.

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

Function Documentation

◆ add_column_collation_dependency()

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

Definition at line 7756 of file tablecmds.c.

7757{
7759 referenced;
7760
7761 /* We know the default collation is pinned, so don't bother recording it */
7763 {
7765 myself.objectId = relid;
7766 myself.objectSubId = attnum;
7768 referenced.objectId = collid;
7769 referenced.objectSubId = 0;
7771 }
7772}
#define OidIsValid(objectId)
Definition c.h:800
Oid collid
int16 attnum
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition pg_depend.c:47
static int fb(int x)

References attnum, ObjectAddress::classId, collid, DEPENDENCY_NORMAL, fb(), 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 7738 of file tablecmds.c.

7739{
7741 referenced;
7742
7744 myself.objectId = relid;
7745 myself.objectSubId = attnum;
7746 referenced.classId = TypeRelationId;
7747 referenced.objectId = typid;
7748 referenced.objectSubId = 0;
7750}

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

Referenced by ATExecAddColumn(), and ATExecAlterColumnType().

◆ addFkConstraint()

static ObjectAddress addFkConstraint ( addFkConstraintSides  fkside,
char constraintname,
Constraint fkconstraint,
Relation  rel,
Relation  pkrel,
Oid  indexOid,
Oid  parentConstr,
int  numfks,
int16 pkattnum,
int16 fkattnum,
Oid pfeqoperators,
Oid ppeqoperators,
Oid ffeqoperators,
int  numfkdelsetcols,
int16 fkdelsetcols,
bool  is_internal,
bool  with_period 
)
static

Definition at line 10749 of file tablecmds.c.

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

References addFkBothSides, addFkReferencedSide, Assert, ChooseConstraintName(), CommandCounterIncrement(), CONSTRAINT_RELATION, ConstraintNameIsUsed(), CreateConstraintEntry(), DEPENDENCY_INTERNAL, DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, ereport, errcode(), errmsg(), ERROR, fb(), InvalidOid, NIL, ObjectAddressSet, OidIsValid, pstrdup(), RelationData::rd_rel, recordDependencyOn(), RelationGetNamespace, RelationGetRelationName, and RelationGetRelid.

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

◆ addFkRecurseReferenced()

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

Definition at line 10927 of file tablecmds.c.

10936{
10939
10942
10943 /*
10944 * Create action triggers to enforce the constraint, or skip them if the
10945 * constraint is NOT ENFORCED.
10946 */
10947 if (fkconstraint->is_enforced)
10951 parentConstr, indexOid,
10954
10955 /*
10956 * If the referenced table is partitioned, recurse on ourselves to handle
10957 * each partition. We need one pg_constraint row created for each
10958 * partition in addition to the pg_constraint row for the parent table.
10959 */
10960 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10961 {
10963
10964 for (int i = 0; i < pd->nparts; i++)
10965 {
10966 Relation partRel;
10967 AttrMap *map;
10970 ObjectAddress address;
10971
10972 /* XXX would it be better to acquire these locks beforehand? */
10973 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
10974
10975 /*
10976 * Map the attribute numbers in the referenced side of the FK
10977 * definition to match the partition's column layout.
10978 */
10981 false);
10982 if (map)
10983 {
10985 for (int j = 0; j < numfks; j++)
10986 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
10987 }
10988 else
10990
10991 /* Determine the index to use at this level */
10992 partIndexId = index_get_partition(partRel, indexOid);
10993 if (!OidIsValid(partIndexId))
10994 elog(ERROR, "index for %u not found in partition %s",
10995 indexOid, RelationGetRelationName(partRel));
10996
10997 /* Create entry at this level ... */
10999 fkconstraint->conname, fkconstraint, rel,
11000 partRel, partIndexId, parentConstr,
11004 fkdelsetcols, true, with_period);
11005 /* ... and recurse to our children */
11007 partIndexId, address.objectId, numfks,
11013 with_period);
11014
11015 /* Done -- clean up (but keep the lock) */
11016 table_close(partRel, NoLock);
11017 if (map)
11018 {
11020 free_attrmap(map);
11021 }
11022 }
11023 }
11024}
void free_attrmap(AttrMap *map)
Definition attmap.c:56
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition attmap.c:261
int16 AttrNumber
Definition attnum.h:21
#define elog(elevel,...)
Definition elog.h:226
#define palloc_array(type, count)
Definition fe_memutils.h:76
int j
Definition isn.c:78
int i
Definition isn.c:77
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
Definition lmgr.c:334
#define NoLock
Definition lockdefs.h:34
#define ShareRowExclusiveLock
Definition lockdefs.h:41
void pfree(void *pointer)
Definition mcxt.c:1616
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition partdesc.c:71
Oid index_get_partition(Relation partition, Oid indexId)
Definition partition.c:176
#define RelationGetDescr(relation)
Definition rel.h:540
AttrNumber * attnums
Definition attmap.h:36
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
static ObjectAddress addFkConstraint(addFkConstraintSides fkside, char *constraintname, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool is_internal, bool with_period)
static void addFkRecurseReferenced(Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, Oid parentDelTrigger, Oid parentUpdTrigger, bool with_period)

References addFkConstraint(), addFkRecurseReferenced(), addFkReferencedSide, Assert, AttrMap::attnums, build_attrmap_by_name_if_req(), CheckRelationLockedByMe(), createForeignKeyActionTriggers(), elog, ERROR, fb(), free_attrmap(), i, index_get_partition(), InvalidOid, j, NoLock, PartitionDescData::nparts, ObjectAddress::objectId, OidIsValid, PartitionDescData::oids, palloc_array, pfree(), RelationGetDescr, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, ShareRowExclusiveLock, table_close(), and table_open().

Referenced by addFkRecurseReferenced(), ATAddForeignKeyConstraint(), CloneFkReferenced(), and DetachPartitionFinalize().

◆ addFkRecurseReferencing()

static void addFkRecurseReferencing ( List **  wqueue,
Constraint fkconstraint,
Relation  rel,
Relation  pkrel,
Oid  indexOid,
Oid  parentConstr,
int  numfks,
int16 pkattnum,
int16 fkattnum,
Oid pfeqoperators,
Oid ppeqoperators,
Oid ffeqoperators,
int  numfkdelsetcols,
int16 fkdelsetcols,
bool  old_check_ok,
LOCKMODE  lockmode,
Oid  parentInsTrigger,
Oid  parentUpdTrigger,
bool  with_period 
)
static

Definition at line 11065 of file tablecmds.c.

11073{
11076
11080
11081 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11082 ereport(ERROR,
11084 errmsg("foreign key constraints are not supported on foreign tables")));
11085
11086 /*
11087 * Add check triggers if the constraint is ENFORCED, and if needed,
11088 * schedule them to be checked in Phase 3.
11089 *
11090 * If the relation is partitioned, drill down to do it to its partitions.
11091 */
11092 if (fkconstraint->is_enforced)
11097 indexOid,
11100
11101 if (rel->rd_rel->relkind == RELKIND_RELATION)
11102 {
11103 /*
11104 * Tell Phase 3 to check that the constraint is satisfied by existing
11105 * rows. We can skip this during table creation, when constraint is
11106 * specified as NOT ENFORCED, or when requested explicitly by
11107 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11108 * recreating a constraint following a SET DATA TYPE operation that
11109 * did not impugn its validity.
11110 */
11111 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11112 fkconstraint->is_enforced)
11113 {
11115 AlteredTableInfo *tab;
11116
11117 tab = ATGetQueueEntry(wqueue, rel);
11118
11121 newcon->contype = CONSTR_FOREIGN;
11122 newcon->refrelid = RelationGetRelid(pkrel);
11123 newcon->refindid = indexOid;
11124 newcon->conid = parentConstr;
11125 newcon->conwithperiod = fkconstraint->fk_with_period;
11126 newcon->qual = (Node *) fkconstraint;
11127
11128 tab->constraints = lappend(tab->constraints, newcon);
11129 }
11130 }
11131 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11132 {
11135
11136 /*
11137 * Triggers of the foreign keys will be manipulated a bunch of times
11138 * in the loop below. To avoid repeatedly opening/closing the trigger
11139 * catalog relation, we open it here and pass it to the subroutines
11140 * called below.
11141 */
11143
11144 /*
11145 * Recurse to take appropriate action on each partition; either we
11146 * find an existing constraint to reparent to ours, or we create a new
11147 * one.
11148 */
11149 for (int i = 0; i < pd->nparts; i++)
11150 {
11151 Relation partition = table_open(pd->oids[i], lockmode);
11152 List *partFKs;
11153 AttrMap *attmap;
11155 bool attached;
11156 ObjectAddress address;
11157
11159
11161 RelationGetDescr(rel),
11162 false);
11163 for (int j = 0; j < numfks; j++)
11164 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11165
11166 /* Check whether an existing constraint can be repurposed */
11168 attached = false;
11170 {
11172 fk,
11173 partition,
11175 numfks,
11177 pkattnum,
11181 trigrel))
11182 {
11183 attached = true;
11184 break;
11185 }
11186 }
11187 if (attached)
11188 {
11190 continue;
11191 }
11192
11193 /*
11194 * No luck finding a good constraint to reuse; create our own.
11195 */
11197 fkconstraint->conname, fkconstraint,
11198 partition, pkrel, indexOid, parentConstr,
11203 with_period);
11204
11205 /* call ourselves to finalize the creation and we're done */
11207 indexOid,
11208 address.objectId,
11209 numfks,
11210 pkattnum,
11218 lockmode,
11221 with_period);
11222
11224 }
11225
11227 }
11228}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition attmap.c:175
#define palloc0_object(type)
Definition fe_memutils.h:75
List * lappend(List *list, void *datum)
Definition list.c:339
#define RowExclusiveLock
Definition lockdefs.h:38
char * get_constraint_name(Oid conoid)
Definition lsyscache.c:1157
#define copyObject(obj)
Definition nodes.h:232
@ CONSTR_FOREIGN
#define INDEX_MAX_KEYS
#define foreach_node(type, var, lst)
Definition pg_list.h:496
List * RelationGetFKeyList(Relation relation)
Definition relcache.c:4726
Definition pg_list.h:54
Definition nodes.h:135
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition tablecmds.c:6597
static void addFkRecurseReferencing(List **wqueue, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, LOCKMODE lockmode, Oid parentInsTrigger, Oid parentUpdTrigger, bool with_period)
static void CheckAlterTableIsSafe(Relation rel)
Definition tablecmds.c:4457
static void createForeignKeyCheckTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentInsTrigger, Oid parentUpdTrigger, Oid *insertTrigOid, Oid *updateTrigOid)
static bool tryAttachPartitionForeignKey(List **wqueue, ForeignKeyCacheInfo *fk, Relation partition, Oid parentConstrOid, int numfks, AttrNumber *mapped_conkey, AttrNumber *confkey, Oid *conpfeqop, Oid parentInsTrigger, Oid parentUpdTrigger, Relation trigrel)

References addFkConstraint(), addFkRecurseReferencing(), addFkReferencingSide, Assert, ATGetQueueEntry(), build_attrmap_by_name(), CheckAlterTableIsSafe(), CheckRelationLockedByMe(), CONSTR_FOREIGN, AlteredTableInfo::constraints, copyObject, createForeignKeyCheckTriggers(), ereport, errcode(), errmsg(), ERROR, fb(), foreach_node, get_constraint_name(), i, INDEX_MAX_KEYS, InvalidOid, j, lappend(), NoLock, PartitionDescData::nparts, ObjectAddress::objectId, OidIsValid, PartitionDescData::oids, palloc0_object, RelationData::rd_rel, RelationGetDescr, RelationGetFKeyList(), RelationGetPartitionDesc(), RelationGetRelid, RowExclusiveLock, ShareRowExclusiveLock, table_close(), table_open(), and tryAttachPartitionForeignKey().

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

◆ alter_table_type_to_string()

static const char * alter_table_type_to_string ( AlterTableType  cmdtype)
static

Definition at line 6631 of file tablecmds.c.

6632{
6633 switch (cmdtype)
6634 {
6635 case AT_AddColumn:
6636 case AT_AddColumnToView:
6637 return "ADD COLUMN";
6638 case AT_ColumnDefault:
6640 return "ALTER COLUMN ... SET DEFAULT";
6641 case AT_DropNotNull:
6642 return "ALTER COLUMN ... DROP NOT NULL";
6643 case AT_SetNotNull:
6644 return "ALTER COLUMN ... SET NOT NULL";
6645 case AT_SetExpression:
6646 return "ALTER COLUMN ... SET EXPRESSION";
6647 case AT_DropExpression:
6648 return "ALTER COLUMN ... DROP EXPRESSION";
6649 case AT_SetStatistics:
6650 return "ALTER COLUMN ... SET STATISTICS";
6651 case AT_SetOptions:
6652 return "ALTER COLUMN ... SET";
6653 case AT_ResetOptions:
6654 return "ALTER COLUMN ... RESET";
6655 case AT_SetStorage:
6656 return "ALTER COLUMN ... SET STORAGE";
6657 case AT_SetCompression:
6658 return "ALTER COLUMN ... SET COMPRESSION";
6659 case AT_DropColumn:
6660 return "DROP COLUMN";
6661 case AT_AddIndex:
6662 case AT_ReAddIndex:
6663 return NULL; /* not real grammar */
6664 case AT_AddConstraint:
6665 case AT_ReAddConstraint:
6668 return "ADD CONSTRAINT";
6669 case AT_AlterConstraint:
6670 return "ALTER CONSTRAINT";
6672 return "VALIDATE CONSTRAINT";
6673 case AT_DropConstraint:
6674 return "DROP CONSTRAINT";
6675 case AT_ReAddComment:
6676 return NULL; /* not real grammar */
6677 case AT_AlterColumnType:
6678 return "ALTER COLUMN ... SET DATA TYPE";
6680 return "ALTER COLUMN ... OPTIONS";
6681 case AT_ChangeOwner:
6682 return "OWNER TO";
6683 case AT_ClusterOn:
6684 return "CLUSTER ON";
6685 case AT_DropCluster:
6686 return "SET WITHOUT CLUSTER";
6687 case AT_SetAccessMethod:
6688 return "SET ACCESS METHOD";
6689 case AT_SetLogged:
6690 return "SET LOGGED";
6691 case AT_SetUnLogged:
6692 return "SET UNLOGGED";
6693 case AT_DropOids:
6694 return "SET WITHOUT OIDS";
6695 case AT_SetTableSpace:
6696 return "SET TABLESPACE";
6697 case AT_SetRelOptions:
6698 return "SET";
6699 case AT_ResetRelOptions:
6700 return "RESET";
6702 return NULL; /* not real grammar */
6703 case AT_EnableTrig:
6704 return "ENABLE TRIGGER";
6706 return "ENABLE ALWAYS TRIGGER";
6708 return "ENABLE REPLICA TRIGGER";
6709 case AT_DisableTrig:
6710 return "DISABLE TRIGGER";
6711 case AT_EnableTrigAll:
6712 return "ENABLE TRIGGER ALL";
6713 case AT_DisableTrigAll:
6714 return "DISABLE TRIGGER ALL";
6715 case AT_EnableTrigUser:
6716 return "ENABLE TRIGGER USER";
6717 case AT_DisableTrigUser:
6718 return "DISABLE TRIGGER USER";
6719 case AT_EnableRule:
6720 return "ENABLE RULE";
6722 return "ENABLE ALWAYS RULE";
6724 return "ENABLE REPLICA RULE";
6725 case AT_DisableRule:
6726 return "DISABLE RULE";
6727 case AT_AddInherit:
6728 return "INHERIT";
6729 case AT_DropInherit:
6730 return "NO INHERIT";
6731 case AT_AddOf:
6732 return "OF";
6733 case AT_DropOf:
6734 return "NOT OF";
6735 case AT_ReplicaIdentity:
6736 return "REPLICA IDENTITY";
6738 return "ENABLE ROW SECURITY";
6740 return "DISABLE ROW SECURITY";
6742 return "FORCE ROW SECURITY";
6744 return "NO FORCE ROW SECURITY";
6745 case AT_GenericOptions:
6746 return "OPTIONS";
6747 case AT_AttachPartition:
6748 return "ATTACH PARTITION";
6749 case AT_DetachPartition:
6750 return "DETACH PARTITION";
6752 return "DETACH PARTITION ... FINALIZE";
6753 case AT_MergePartitions:
6754 return "MERGE PARTITIONS";
6755 case AT_SplitPartition:
6756 return "SPLIT PARTITION";
6757 case AT_AddIdentity:
6758 return "ALTER COLUMN ... ADD IDENTITY";
6759 case AT_SetIdentity:
6760 return "ALTER COLUMN ... SET";
6761 case AT_DropIdentity:
6762 return "ALTER COLUMN ... DROP IDENTITY";
6763 case AT_ReAddStatistics:
6764 return NULL; /* not real grammar */
6765 }
6766
6767 return NULL;
6768}
@ AT_AddIndexConstraint
@ AT_MergePartitions
@ AT_DropOf
@ AT_SetOptions
@ AT_DropIdentity
@ AT_DisableTrigUser
@ AT_DropNotNull
@ AT_AddOf
@ AT_ResetOptions
@ AT_ReplicaIdentity
@ AT_ReplaceRelOptions
@ AT_EnableRowSecurity
@ AT_AddColumnToView
@ AT_ResetRelOptions
@ AT_EnableReplicaTrig
@ AT_DropOids
@ AT_SetIdentity
@ AT_ReAddStatistics
@ AT_SetUnLogged
@ AT_DisableTrig
@ AT_SetCompression
@ AT_DropExpression
@ AT_AddIndex
@ AT_EnableReplicaRule
@ AT_ReAddIndex
@ AT_DropConstraint
@ AT_SetNotNull
@ AT_ClusterOn
@ AT_AddIdentity
@ AT_ForceRowSecurity
@ AT_EnableAlwaysRule
@ AT_SetAccessMethod
@ AT_AlterColumnType
@ AT_DetachPartitionFinalize
@ AT_AddInherit
@ AT_ReAddDomainConstraint
@ AT_EnableTrig
@ AT_DropColumn
@ AT_ReAddComment
@ AT_AlterColumnGenericOptions
@ AT_DisableTrigAll
@ AT_EnableRule
@ AT_NoForceRowSecurity
@ AT_DetachPartition
@ AT_SetStatistics
@ AT_AttachPartition
@ AT_AddConstraint
@ AT_DropInherit
@ AT_EnableAlwaysTrig
@ AT_SetLogged
@ AT_SetStorage
@ AT_DisableRule
@ AT_DisableRowSecurity
@ AT_SetRelOptions
@ AT_ChangeOwner
@ AT_EnableTrigUser
@ AT_SetExpression
@ AT_ReAddConstraint
@ AT_SetTableSpace
@ AT_GenericOptions
@ AT_ColumnDefault
@ AT_CookedColumnDefault
@ AT_AlterConstraint
@ AT_EnableTrigAll
@ AT_SplitPartition
@ AT_DropCluster
@ AT_ValidateConstraint
@ AT_AddColumn

References AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AddInherit, AT_AddOf, AT_AlterColumnGenericOptions, AT_AlterColumnType, AT_AlterConstraint, AT_AttachPartition, AT_ChangeOwner, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_MergePartitions, AT_NoForceRowSecurity, AT_ReAddComment, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReAddIndex, AT_ReAddStatistics, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, AT_SplitPartition, AT_ValidateConstraint, and fb().

Referenced by ATSimplePermissions().

◆ AlterConstrDeferrabilityRecurse()

static void AlterConstrDeferrabilityRecurse ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  tgrel,
Relation  rel,
HeapTuple  contuple,
bool  recurse,
List **  otherrelids,
LOCKMODE  lockmode 
)
static

Definition at line 12839 of file tablecmds.c.

12843{
12845 Oid conoid;
12846 ScanKeyData pkey;
12849
12851 conoid = currcon->oid;
12852
12853 ScanKeyInit(&pkey,
12856 ObjectIdGetDatum(conoid));
12857
12859 true, NULL, 1, &pkey);
12860
12862 {
12865
12866 childrel = table_open(childcon->conrelid, lockmode);
12867
12869 childtup, recurse, otherrelids, lockmode);
12871 }
12872
12874}
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
#define HeapTupleIsValid(tuple)
Definition htup.h:78
static void * GETSTRUCT(const HeapTupleData *tuple)
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:262
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
#define BTEqualStrategyNumber
Definition stratnum.h:31
static bool ATExecAlterConstrDeferrability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, List **otherrelids, LOCKMODE lockmode)

References ATExecAlterConstrDeferrability(), BTEqualStrategyNumber, fb(), Form_pg_constraint, GETSTRUCT(), HeapTupleIsValid, NoLock, ObjectIdGetDatum(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecAlterConstrDeferrability().

◆ AlterConstrEnforceabilityRecurse()

static void AlterConstrEnforceabilityRecurse ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  tgrel,
Oid  fkrelid,
Oid  pkrelid,
HeapTuple  contuple,
LOCKMODE  lockmode,
Oid  ReferencedParentDelTrigger,
Oid  ReferencedParentUpdTrigger,
Oid  ReferencingParentInsTrigger,
Oid  ReferencingParentUpdTrigger 
)
static

Definition at line 12790 of file tablecmds.c.

12798{
12800 Oid conoid;
12801 ScanKeyData pkey;
12804
12806 conoid = currcon->oid;
12807
12808 ScanKeyInit(&pkey,
12811 ObjectIdGetDatum(conoid));
12812
12814 true, NULL, 1, &pkey);
12815
12818 pkrelid, childtup, lockmode,
12823
12825}
static bool ATExecAlterConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)

References ATExecAlterConstrEnforceability(), BTEqualStrategyNumber, fb(), Form_pg_constraint, GETSTRUCT(), HeapTupleIsValid, ObjectIdGetDatum(), ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by ATExecAlterConstrEnforceability().

◆ AlterConstrTriggerDeferrability()

static void AlterConstrTriggerDeferrability ( Oid  conoid,
Relation  tgrel,
Relation  rel,
bool  deferrable,
bool  initdeferred,
List **  otherrelids 
)
static

Definition at line 12721 of file tablecmds.c.

12724{
12728
12732 ObjectIdGetDatum(conoid));
12734 NULL, 1, &tgkey);
12736 {
12740
12741 /*
12742 * Remember OIDs of other relation(s) involved in FK constraint.
12743 * (Note: it's likely that we could skip forcing a relcache inval for
12744 * other rels that don't have a trigger whose properties change, but
12745 * let's be conservative.)
12746 */
12747 if (tgform->tgrelid != RelationGetRelid(rel))
12749 tgform->tgrelid);
12750
12751 /*
12752 * Update enable status and deferrability of RI_FKey_noaction_del,
12753 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12754 * triggers, but not others; see createForeignKeyActionTriggers and
12755 * CreateFKCheckTrigger.
12756 */
12757 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12758 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12759 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12760 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12761 continue;
12762
12765
12766 copy_tg->tgdeferrable = deferrable;
12767 copy_tg->tginitdeferred = initdeferred;
12769
12771
12773 }
12774
12776}
HeapTuple heap_copytuple(HeapTuple tuple)
Definition heaptuple.c:778
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1435
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition indexing.c:313
List * list_append_unique_oid(List *list, Oid datum)
Definition list.c:1380
#define InvokeObjectPostAlterHook(classId, objectId, subId)
END_CATALOG_STRUCT typedef FormData_pg_trigger * Form_pg_trigger
Definition pg_trigger.h:84

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

Referenced by ATExecAlterConstrDeferrability().

◆ AlterConstrUpdateConstraintEntry()

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

Definition at line 12881 of file tablecmds.c.

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

References Assert, CacheInvalidateRelcacheByRelid(), CatalogTupleUpdate(), fb(), Form_pg_constraint, GETSTRUCT(), heap_copytuple(), heap_freetuple(), and InvokeObjectPostAlterHook.

Referenced by ATExecAlterConstrDeferrability(), ATExecAlterConstrEnforceability(), and ATExecAlterConstrInheritability().

◆ AlterIndexNamespaces()

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

Definition at line 19156 of file tablecmds.c.

19158{
19159 List *indexList;
19160 ListCell *l;
19161
19163
19164 foreach(l, indexList)
19165 {
19166 Oid indexOid = lfirst_oid(l);
19168
19170 thisobj.objectId = indexOid;
19171 thisobj.objectSubId = 0;
19172
19173 /*
19174 * Note: currently, the index will not have its own dependency on the
19175 * namespace, so we don't need to do changeDependencyFor(). There's no
19176 * row type in pg_type, either.
19177 *
19178 * XXX this objsMoved test may be pointless -- surely we have a single
19179 * dependency link from a relation to each index?
19180 */
19182 {
19185 false, objsMoved);
19187 }
19188 }
19189
19191}
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
void list_free(List *list)
Definition list.c:1546
#define lfirst_oid(lc)
Definition pg_list.h:174
List * RelationGetIndexList(Relation relation)
Definition relcache.c:4831
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)

References add_exact_object_address(), AlterRelationNamespaceInternal(), ObjectAddress::classId, fb(), lfirst_oid, list_free(), object_address_present(), and RelationGetIndexList().

Referenced by AlterTableNamespaceInternal().

◆ AlterRelationNamespaceInternal()

void AlterRelationNamespaceInternal ( Relation  classRel,
Oid  relOid,
Oid  oldNspOid,
Oid  newNspOid,
bool  hasDependEntry,
ObjectAddresses objsMoved 
)

Definition at line 19079 of file tablecmds.c.

19083{
19087 bool already_done = false;
19088
19089 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19092 elog(ERROR, "cache lookup failed for relation %u", relOid);
19094
19095 Assert(classForm->relnamespace == oldNspOid);
19096
19097 thisobj.classId = RelationRelationId;
19098 thisobj.objectId = relOid;
19099 thisobj.objectSubId = 0;
19100
19101 /*
19102 * If the object has already been moved, don't move it again. If it's
19103 * already in the right place, don't move it, but still fire the object
19104 * access hook.
19105 */
19107 if (!already_done && oldNspOid != newNspOid)
19108 {
19109 ItemPointerData otid = classTup->t_self;
19110
19111 /* check for duplicate name (more friendly than unique-index failure) */
19112 if (get_relname_relid(NameStr(classForm->relname),
19114 ereport(ERROR,
19116 errmsg("relation \"%s\" already exists in schema \"%s\"",
19117 NameStr(classForm->relname),
19119
19120 /* classTup is a copy, so OK to scribble on */
19121 classForm->relnamespace = newNspOid;
19122
19125
19126
19127 /* Update dependency on schema if caller said so */
19128 if (hasDependEntry &&
19130 relOid,
19132 oldNspOid,
19133 newNspOid) != 1)
19134 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19135 NameStr(classForm->relname));
19136 }
19137 else
19139 if (!already_done)
19140 {
19142
19144 }
19145
19147}
#define NameStr(name)
Definition c.h:777
void UnlockTuple(Relation relation, const ItemPointerData *tid, LOCKMODE lockmode)
Definition lmgr.c:601
#define InplaceUpdateTupleLock
Definition lockdefs.h:48
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3518
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition lsyscache.c:2035
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition pg_depend.c:459
HeapTuple SearchSysCacheLockedCopy1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:399

References add_exact_object_address(), Assert, CatalogTupleUpdate(), changeDependencyFor(), elog, ereport, errcode(), errmsg(), ERROR, fb(), get_namespace_name(), get_relname_relid(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InplaceUpdateTupleLock, InvalidOid, InvokeObjectPostAlterHook, NameStr, object_address_present(), ObjectIdGetDatum(), SearchSysCacheLockedCopy1(), and UnlockTuple().

Referenced by AlterIndexNamespaces(), AlterSeqNamespaces(), AlterTableNamespaceInternal(), and AlterTypeNamespaceInternal().

◆ AlterSeqNamespaces()

static void AlterSeqNamespaces ( Relation  classRel,
Relation  rel,
Oid  oldNspOid,
Oid  newNspOid,
ObjectAddresses objsMoved,
LOCKMODE  lockmode 
)
static

Definition at line 19201 of file tablecmds.c.

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

References AccessShareLock, AlterRelationNamespaceInternal(), Assert, BTEqualStrategyNumber, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, fb(), Form_pg_depend, GETSTRUCT(), HeapTupleIsValid, InvalidOid, 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 4542 of file tablecmds.c.

4544{
4545 Relation rel;
4546
4547 /* Caller is required to provide an adequate lock. */
4548 rel = relation_open(context->relid, NoLock);
4549
4551
4552 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4553}
#define stmt
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:4883

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4616 of file tablecmds.c.

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

References AccessExclusiveLock, AlterTableGetRelOptionsLockLevel(), AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AddInherit, AT_AddOf, AT_AlterColumnGenericOptions, AT_AlterColumnType, AT_AlterConstraint, AT_AttachPartition, AT_ChangeOwner, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_MergePartitions, AT_NoForceRowSecurity, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, AT_SplitPartition, AT_ValidateConstraint, CONSTR_EXCLUSION, CONSTR_FOREIGN, CONSTR_PRIMARY, CONSTR_UNIQUE, Constraint::contype, AlterTableCmd::def, elog, ERROR, fb(), IsA, lfirst, ShareRowExclusiveLock, ShareUpdateExclusiveLock, and AlterTableCmd::subtype.

Referenced by AlterTableInternal(), and ProcessUtilitySlow().

◆ AlterTableInternal()

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

Definition at line 4571 of file tablecmds.c.

4572{
4573 Relation rel;
4574 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4575
4576 rel = relation_open(relid, lockmode);
4577
4579
4580 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4581}
void EventTriggerAlterTableRelid(Oid objectId)
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition tablecmds.c:4616

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4483 of file tablecmds.c.

4484{
4485 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4486 stmt->missing_ok ? RVR_MISSING_OK : 0,
4488 stmt);
4489}
Oid RangeVarGetRelidExtended(const RangeVar *relation, LOCKMODE lockmode, uint32 flags, RangeVarGetRelidCallback callback, void *callback_arg)
Definition namespace.c:442
@ RVR_MISSING_OK
Definition namespace.h:90
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)

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

Referenced by ProcessUtilitySlow().

◆ AlterTableMoveAll()

Oid AlterTableMoveAll ( AlterTableMoveAllStmt stmt)

Definition at line 17012 of file tablecmds.c.

17013{
17014 List *relations = NIL;
17015 ListCell *l;
17016 ScanKeyData key[1];
17017 Relation rel;
17018 TableScanDesc scan;
17019 HeapTuple tuple;
17022 List *role_oids = roleSpecsToIds(stmt->roles);
17023
17024 /* Ensure we were not asked to move something we can't */
17025 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17026 stmt->objtype != OBJECT_MATVIEW)
17027 ereport(ERROR,
17029 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17030
17031 /* Get the orig and new tablespace OIDs */
17032 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17033 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17034
17035 /* Can't move shared relations in to or out of pg_global */
17036 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17039 ereport(ERROR,
17041 errmsg("cannot move relations in to or out of pg_global tablespace")));
17042
17043 /*
17044 * Must have CREATE rights on the new tablespace, unless it is the
17045 * database default tablespace (which all users implicitly have CREATE
17046 * rights on).
17047 */
17049 {
17051
17053 ACL_CREATE);
17054 if (aclresult != ACLCHECK_OK)
17057 }
17058
17059 /*
17060 * Now that the checks are done, check if we should set either to
17061 * InvalidOid because it is our database's default tablespace.
17062 */
17065
17068
17069 /* no-op */
17071 return new_tablespaceoid;
17072
17073 /*
17074 * Walk the list of objects in the tablespace and move them. This will
17075 * only find objects in our database, of course.
17076 */
17077 ScanKeyInit(&key[0],
17081
17083 scan = table_beginscan_catalog(rel, 1, key);
17084 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17085 {
17087 Oid relOid = relForm->oid;
17088
17089 /*
17090 * Do not move objects in pg_catalog as part of this, if an admin
17091 * really wishes to do so, they can issue the individual ALTER
17092 * commands directly.
17093 *
17094 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17095 * (TOAST will be moved with the main table).
17096 */
17097 if (IsCatalogNamespace(relForm->relnamespace) ||
17098 relForm->relisshared ||
17099 isAnyTempNamespace(relForm->relnamespace) ||
17100 IsToastNamespace(relForm->relnamespace))
17101 continue;
17102
17103 /* Only move the object type requested */
17104 if ((stmt->objtype == OBJECT_TABLE &&
17105 relForm->relkind != RELKIND_RELATION &&
17106 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17107 (stmt->objtype == OBJECT_INDEX &&
17108 relForm->relkind != RELKIND_INDEX &&
17109 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17110 (stmt->objtype == OBJECT_MATVIEW &&
17111 relForm->relkind != RELKIND_MATVIEW))
17112 continue;
17113
17114 /* Check if we are only moving objects owned by certain roles */
17115 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17116 continue;
17117
17118 /*
17119 * Handle permissions-checking here since we are locking the tables
17120 * and also to avoid doing a bunch of work only to fail part-way. Note
17121 * that permissions will also be checked by AlterTableInternal().
17122 *
17123 * Caller must be considered an owner on the table to move it.
17124 */
17127 NameStr(relForm->relname));
17128
17129 if (stmt->nowait &&
17131 ereport(ERROR,
17133 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17134 get_namespace_name(relForm->relnamespace),
17135 NameStr(relForm->relname))));
17136 else
17138
17139 /* Add to our list of objects to move */
17140 relations = lappend_oid(relations, relOid);
17141 }
17142
17143 table_endscan(scan);
17145
17146 if (relations == NIL)
17149 errmsg("no matching relations in tablespace \"%s\" found",
17150 orig_tablespaceoid == InvalidOid ? "(database default)" :
17152
17153 /* Everything is locked, loop through and move all of the relations. */
17154 foreach(l, relations)
17155 {
17156 List *cmds = NIL;
17158
17160 cmd->name = stmt->new_tablespacename;
17161
17162 cmds = lappend(cmds, cmd);
17163
17165 /* OID is set by AlterTableInternal */
17166 AlterTableInternal(lfirst_oid(l), cmds, false);
17168 }
17169
17170 return new_tablespaceoid;
17171}
AclResult
Definition acl.h:182
@ ACLCHECK_OK
Definition acl.h:183
@ ACLCHECK_NOT_OWNER
Definition acl.h:185
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2654
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3854
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4108
char * get_tablespace_name(Oid spc_oid)
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
bool IsToastNamespace(Oid namespaceId)
Definition catalog.c:261
bool IsCatalogNamespace(Oid namespaceId)
Definition catalog.c:243
#define NOTICE
Definition elog.h:35
void EventTriggerAlterTableStart(Node *parsetree)
void EventTriggerAlterTableEnd(void)
Oid MyDatabaseTableSpace
Definition globals.c:96
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition heapam.c:1410
List * lappend_oid(List *list, Oid datum)
Definition list.c:375
bool list_member_oid(const List *list, Oid datum)
Definition list.c:722
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:151
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:107
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2153
Oid GetUserId(void)
Definition miscinit.c:469
bool isAnyTempNamespace(Oid namespaceId)
Definition namespace.c:3759
#define makeNode(_type_)
Definition nodes.h:161
ObjectType get_relkind_objtype(char relkind)
@ OBJECT_MATVIEW
@ OBJECT_TABLESPACE
@ OBJECT_INDEX
@ OBJECT_TABLE
#define ACL_CREATE
Definition parsenodes.h:85
@ ForwardScanDirection
Definition sdir.h:28
TableScanDesc table_beginscan_catalog(Relation relation, int nkeys, ScanKeyData *key)
Definition tableam.c:113
static void table_endscan(TableScanDesc scan)
Definition tableam.h:1004
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition tablecmds.c:4571
List * roleSpecsToIds(List *memberNames)
Definition user.c:1665

References AccessExclusiveLock, AccessShareLock, ACL_CREATE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, AlterTableInternal(), AT_SetTableSpace, BTEqualStrategyNumber, ConditionalLockRelationOid(), ereport, errcode(), errmsg(), ERROR, EventTriggerAlterTableEnd(), EventTriggerAlterTableStart(), fb(), ForwardScanDirection, get_namespace_name(), get_rel_relkind(), get_relkind_objtype(), get_tablespace_name(), get_tablespace_oid(), GETSTRUCT(), GetUserId(), heap_getnext(), InvalidOid, isAnyTempNamespace(), IsCatalogNamespace(), IsToastNamespace(), 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 18971 of file tablecmds.c.

18972{
18973 Relation rel;
18974 Oid relid;
18975 Oid oldNspOid;
18976 Oid nspOid;
18977 RangeVar *newrv;
18980
18982 stmt->missing_ok ? RVR_MISSING_OK : 0,
18984 stmt);
18985
18986 if (!OidIsValid(relid))
18987 {
18989 (errmsg("relation \"%s\" does not exist, skipping",
18990 stmt->relation->relname)));
18991 return InvalidObjectAddress;
18992 }
18993
18994 rel = relation_open(relid, NoLock);
18995
18997
18998 /* If it's an owned sequence, disallow moving it by itself. */
18999 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19000 {
19001 Oid tableId;
19002 int32 colId;
19003
19004 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19006 ereport(ERROR,
19008 errmsg("cannot move an owned sequence into another schema"),
19009 errdetail("Sequence \"%s\" is linked to table \"%s\".",
19012 }
19013
19014 /* Get and lock schema OID and check its permissions. */
19015 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19017
19018 /* common checks on switching namespaces */
19020
19024
19026
19027 if (oldschema)
19029
19030 /* close rel, but keep lock until commit */
19031 relation_close(rel, NoLock);
19032
19033 return myself;
19034}
int32_t int32
Definition c.h:554
ObjectAddresses * new_object_addresses(void)
void free_object_addresses(ObjectAddresses *addrs)
int errdetail(const char *fmt,...) pg_attribute_printf(1
char * get_rel_name(Oid relid)
Definition lsyscache.c:2078
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition makefuncs.c:473
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition namespace.c:740
void CheckSetNamespace(Oid oldNspOid, Oid nspOid)
Definition namespace.c:3531
const ObjectAddress InvalidObjectAddress
bool sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
Definition pg_depend.c:901
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)

References AccessExclusiveLock, AlterTableNamespaceInternal(), CheckSetNamespace(), DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), 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 19042 of file tablecmds.c.

19044{
19046
19047 Assert(objsMoved != NULL);
19048
19049 /* OK, modify the pg_class row and pg_depend entry */
19051
19053 nspOid, true, objsMoved);
19054
19055 /* Fix the table's row type too, if it has one */
19056 if (OidIsValid(rel->rd_rel->reltype))
19058 false, /* isImplicitArray */
19059 false, /* ignoreDependent */
19060 false, /* errorOnTableType */
19061 objsMoved);
19062
19063 /* Fix other dependent stuff */
19068 false, objsMoved);
19069
19071}
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)
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition typecmds.c:4197

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

Referenced by AlterObjectNamespace_oid(), and AlterTableNamespace().

◆ ATAddCheckNNConstraint()

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

Definition at line 9939 of file tablecmds.c.

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

References AddRelationNewConstraints(), Assert, AT_AddConstraint, ATAddCheckNNConstraint(), ATGetQueueEntry(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, check_stack_depth(), CheckAlterTableIsSafe(), CommandCounterIncrement(), Constraint::conname, CONSTR_NOTNULL, AlteredTableInfo::constraints, Constraint::contype, copyObject, ereport, errcode(), errmsg(), ERROR, fb(), find_inheritance_children(), InvalidObjectAddress, Constraint::is_no_inherit, lappend(), lfirst, lfirst_oid, list_length(), list_make1, NIL, NoLock, ObjectAddressSet, palloc0_object, RelationGetRelid, set_attnotnull(), Constraint::skip_validation, table_close(), and table_open().

Referenced by ATAddCheckNNConstraint(), and ATExecAddConstraint().

◆ ATAddForeignKeyConstraint()

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

Definition at line 10094 of file tablecmds.c.

10097{
10110 bool with_period;
10112 int i;
10113 int numfks,
10114 numpks,
10116 Oid indexOid;
10117 bool old_check_ok;
10118 ObjectAddress address;
10120
10121 /*
10122 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10123 * delete rows out from under us.
10124 */
10125 if (OidIsValid(fkconstraint->old_pktable_oid))
10127 else
10129
10130 /*
10131 * Validity checks (permission checks wait till we have the column
10132 * numbers)
10133 */
10134 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10135 ereport(ERROR,
10137 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10140
10141 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10142 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10143 ereport(ERROR,
10145 errmsg("referenced relation \"%s\" is not a table",
10147
10149 ereport(ERROR,
10151 errmsg("permission denied: \"%s\" is a system catalog",
10153
10154 /*
10155 * References from permanent or unlogged tables to temp tables, and from
10156 * permanent tables to unlogged tables, are disallowed because the
10157 * referenced data can vanish out from under us. References from temp
10158 * tables to any other table type are also disallowed, because other
10159 * backends might need to run the RI triggers on the perm table, but they
10160 * can't reliably see tuples in the local buffers of other backends.
10161 */
10162 switch (rel->rd_rel->relpersistence)
10163 {
10166 ereport(ERROR,
10168 errmsg("constraints on permanent tables may reference only permanent tables")));
10169 break;
10172 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10173 ereport(ERROR,
10175 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10176 break;
10178 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10179 ereport(ERROR,
10181 errmsg("constraints on temporary tables may reference only temporary tables")));
10182 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10183 ereport(ERROR,
10185 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10186 break;
10187 }
10188
10189 /*
10190 * Look up the referencing attributes to make sure they exist, and record
10191 * their attnums and type and collation OIDs.
10192 */
10194 fkconstraint->fk_attrs,
10196 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10197 if (with_period && !fkconstraint->fk_with_period)
10198 ereport(ERROR,
10200 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10201
10203 fkconstraint->fk_del_set_cols,
10208 fkconstraint->fk_del_set_cols);
10209
10210 /*
10211 * If the attribute list for the referenced table was omitted, lookup the
10212 * definition of the primary key and use it. Otherwise, validate the
10213 * supplied attribute list. In either case, discover the index OID and
10214 * index opclasses, and the attnums and type and collation OIDs of the
10215 * attributes.
10216 */
10217 if (fkconstraint->pk_attrs == NIL)
10218 {
10220 &fkconstraint->pk_attrs,
10223
10224 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10225 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10226 ereport(ERROR,
10228 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10229 }
10230 else
10231 {
10233 fkconstraint->pk_attrs,
10235
10236 /* Since we got pk_attrs, one should be a period. */
10237 if (with_period && !fkconstraint->pk_with_period)
10238 ereport(ERROR,
10240 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10241
10242 /* Look for an index matching the column list */
10245 }
10246
10247 /*
10248 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10249 * must use PERIOD.
10250 */
10252 ereport(ERROR,
10254 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10255
10256 /*
10257 * Now we can check permissions.
10258 */
10260
10261 /*
10262 * Check some things for generated columns.
10263 */
10264 for (i = 0; i < numfks; i++)
10265 {
10266 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10267
10268 if (attgenerated)
10269 {
10270 /*
10271 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10272 */
10273 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10274 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10275 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10276 ereport(ERROR,
10278 errmsg("invalid %s action for foreign key constraint containing generated column",
10279 "ON UPDATE")));
10280 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10281 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10282 ereport(ERROR,
10284 errmsg("invalid %s action for foreign key constraint containing generated column",
10285 "ON DELETE")));
10286 }
10287
10288 /*
10289 * FKs on virtual columns are not supported. This would require
10290 * various additional support in ri_triggers.c, including special
10291 * handling in ri_NullCheck(), ri_KeysEqual(),
10292 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10293 * as NULL there). Also not really practical as long as you can't
10294 * index virtual columns.
10295 */
10296 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10297 ereport(ERROR,
10299 errmsg("foreign key constraints on virtual generated columns are not supported")));
10300 }
10301
10302 /*
10303 * Some actions are currently unsupported for foreign keys using PERIOD.
10304 */
10305 if (fkconstraint->fk_with_period)
10306 {
10307 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10308 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10309 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10310 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10311 ereport(ERROR,
10313 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10314 "ON UPDATE"));
10315
10316 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10317 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10318 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10319 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10320 ereport(ERROR,
10322 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10323 "ON DELETE"));
10324 }
10325
10326 /*
10327 * Look up the equality operators to use in the constraint.
10328 *
10329 * Note that we have to be careful about the difference between the actual
10330 * PK column type and the opclass' declared input type, which might be
10331 * only binary-compatible with it. The declared opcintype is the right
10332 * thing to probe pg_amop with.
10333 */
10334 if (numfks != numpks)
10335 ereport(ERROR,
10337 errmsg("number of referencing and referenced columns for foreign key disagree")));
10338
10339 /*
10340 * On the strength of a previous constraint, we might avoid scanning
10341 * tables to validate this one. See below.
10342 */
10343 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10344 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10345
10346 for (i = 0; i < numpks; i++)
10347 {
10348 Oid pktype = pktypoid[i];
10349 Oid fktype = fktypoid[i];
10350 Oid fktyped;
10351 Oid pkcoll = pkcolloid[i];
10352 Oid fkcoll = fkcolloid[i];
10355 Oid amid;
10356 Oid opfamily;
10357 Oid opcintype;
10358 bool for_overlaps;
10359 CompareType cmptype;
10360 Oid pfeqop;
10361 Oid ppeqop;
10362 Oid ffeqop;
10365
10366 /* We need several fields out of the pg_opclass entry */
10369 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10371 amid = cla_tup->opcmethod;
10372 opfamily = cla_tup->opcfamily;
10373 opcintype = cla_tup->opcintype;
10375
10376 /*
10377 * Get strategy number from index AM.
10378 *
10379 * For a normal foreign-key constraint, this should not fail, since we
10380 * already checked that the index is unique and should therefore have
10381 * appropriate equal operators. For a period foreign key, this could
10382 * fail if we selected a non-matching exclusion constraint earlier.
10383 * (XXX Maybe we should do these lookups earlier so we don't end up
10384 * doing that.)
10385 */
10386 for_overlaps = with_period && i == numpks - 1;
10388 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10390 ereport(ERROR,
10393 ? errmsg("could not identify an overlaps operator for foreign key")
10394 : errmsg("could not identify an equality operator for foreign key"),
10395 errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10396 cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10397
10398 /*
10399 * There had better be a primary equality operator for the index.
10400 * We'll use it for PK = PK comparisons.
10401 */
10402 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10403 eqstrategy);
10404
10405 if (!OidIsValid(ppeqop))
10406 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10407 eqstrategy, opcintype, opcintype, opfamily);
10408
10409 /*
10410 * Are there equality operators that take exactly the FK type? Assume
10411 * we should look through any domain here.
10412 */
10414
10415 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10416 eqstrategy);
10417 if (OidIsValid(pfeqop))
10418 {
10421 eqstrategy);
10422 }
10423 else
10424 {
10425 /* keep compiler quiet */
10428 }
10429
10430 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10431 {
10432 /*
10433 * Otherwise, look for an implicit cast from the FK type to the
10434 * opcintype, and if found, use the primary equality operator.
10435 * This is a bit tricky because opcintype might be a polymorphic
10436 * type such as ANYARRAY or ANYENUM; so what we have to test is
10437 * whether the two actual column types can be concurrently cast to
10438 * that type. (Otherwise, we'd fail to reject combinations such
10439 * as int[] and point[].)
10440 */
10441 Oid input_typeids[2];
10443
10444 input_typeids[0] = pktype;
10445 input_typeids[1] = fktype;
10446 target_typeids[0] = opcintype;
10447 target_typeids[1] = opcintype;
10450 {
10451 pfeqop = ffeqop = ppeqop;
10452 pfeqop_right = opcintype;
10453 }
10454 }
10455
10456 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10457 ereport(ERROR,
10459 errmsg("foreign key constraint \"%s\" cannot be implemented",
10460 fkconstraint->conname),
10461 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10462 "are of incompatible types: %s and %s.",
10463 strVal(list_nth(fkconstraint->fk_attrs, i)),
10464 strVal(list_nth(fkconstraint->pk_attrs, i)),
10467
10468 /*
10469 * This shouldn't be possible, but better check to make sure we have a
10470 * consistent state for the check below.
10471 */
10473 elog(ERROR, "key columns are not both collatable");
10474
10476 {
10477 bool pkcolldet;
10478 bool fkcolldet;
10479
10482
10483 /*
10484 * SQL requires that both collations are the same. This is
10485 * because we need a consistent notion of equality on both
10486 * columns. We relax this by allowing different collations if
10487 * they are both deterministic. (This is also for backward
10488 * compatibility, because PostgreSQL has always allowed this.)
10489 */
10490 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10491 ereport(ERROR,
10493 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10494 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10495 "have incompatible collations: \"%s\" and \"%s\". "
10496 "If either collation is nondeterministic, then both collations have to be the same.",
10497 strVal(list_nth(fkconstraint->fk_attrs, i)),
10498 strVal(list_nth(fkconstraint->pk_attrs, i)),
10501 }
10502
10503 if (old_check_ok)
10504 {
10505 /*
10506 * When a pfeqop changes, revalidate the constraint. We could
10507 * permit intra-opfamily changes, but that adds subtle complexity
10508 * without any concrete benefit for core types. We need not
10509 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10510 */
10512 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10514 }
10515 if (old_check_ok)
10516 {
10526 fkattnum[i] - 1);
10527
10528 /*
10529 * Identify coercion pathways from each of the old and new FK-side
10530 * column types to the right (foreign) operand type of the pfeqop.
10531 * We may assume that pg_constraint.conkey is not changing.
10532 */
10533 old_fktype = attr->atttypid;
10536 &old_castfunc);
10538 &new_castfunc);
10539
10540 old_fkcoll = attr->attcollation;
10542
10543 /*
10544 * Upon a change to the cast from the FK column to its pfeqop
10545 * operand, revalidate the constraint. For this evaluation, a
10546 * binary coercion cast is equivalent to no cast at all. While
10547 * type implementors should design implicit casts with an eye
10548 * toward consistency of operations like equality, we cannot
10549 * assume here that they have done so.
10550 *
10551 * A function with a polymorphic argument could change behavior
10552 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10553 * when the cast destination is polymorphic, we only avoid
10554 * revalidation if the input type has not changed at all. Given
10555 * just the core data types and operator classes, this requirement
10556 * prevents no would-be optimizations.
10557 *
10558 * If the cast converts from a base type to a domain thereon, then
10559 * that domain type must be the opcintype of the unique index.
10560 * Necessarily, the primary key column must then be of the domain
10561 * type. Since the constraint was previously valid, all values on
10562 * the foreign side necessarily exist on the primary side and in
10563 * turn conform to the domain. Consequently, we need not treat
10564 * domains specially here.
10565 *
10566 * If the collation changes, revalidation is required, unless both
10567 * collations are deterministic, because those share the same
10568 * notion of equality (because texteq reduces to bitwise
10569 * equality).
10570 *
10571 * We need not directly consider the PK type. It's necessarily
10572 * binary coercible to the opcintype of the unique index column,
10573 * and ri_triggers.c will only deal with PK datums in terms of
10574 * that opcintype. Changing the opcintype also changes pfeqop.
10575 */
10579 new_fktype == old_fktype) &&
10580 (new_fkcoll == old_fkcoll ||
10582 }
10583
10587 }
10588
10589 /*
10590 * For FKs with PERIOD we need additional operators to check whether the
10591 * referencing row's range is contained by the aggregated ranges of the
10592 * referenced row(s). For rangetypes and multirangetypes this is
10593 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10594 * support for now. FKs will look these up at "runtime", but we should
10595 * make sure the lookup works here, even if we don't use the values.
10596 */
10597 if (with_period)
10598 {
10602
10605 }
10606
10607 /* First, create the constraint catalog entry itself. */
10609 fkconstraint->conname, fkconstraint, rel, pkrel,
10610 indexOid,
10611 InvalidOid, /* no parent constraint */
10612 numfks,
10613 pkattnum,
10614 fkattnum,
10620 false,
10621 with_period);
10622
10623 /* Next process the action triggers at the referenced side and recurse */
10625 indexOid,
10626 address.objectId,
10627 numfks,
10628 pkattnum,
10629 fkattnum,
10637 with_period);
10638
10639 /* Lastly create the check triggers at the referencing side and recurse */
10641 indexOid,
10642 address.objectId,
10643 numfks,
10644 pkattnum,
10645 fkattnum,
10652 lockmode,
10654 with_period);
10655
10656 /*
10657 * Done. Close pk table, but keep lock until we've committed.
10658 */
10660
10661 return address;
10662}
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition amapi.c:161
char * get_am_name(Oid amOid)
Definition amcmds.c:192
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
CompareType
Definition cmptype.h:32
@ COMPARE_OVERLAP
Definition cmptype.h:40
@ COMPARE_EQ
Definition cmptype.h:36
char * format_type_be(Oid type_oid)
bool allowSystemTableMods
Definition globals.c:130
#define false
char * get_collation_name(Oid colloid)
Definition lsyscache.c:1111
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition lsyscache.c:168
bool get_collation_isdeterministic(Oid colloid)
Definition lsyscache.c:1130
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition lsyscache.c:1403
Oid getBaseType(Oid typid)
Definition lsyscache.c:2673
bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids, CoercionContext ccontext)
CoercionPathType
#define FKCONSTR_ACTION_RESTRICT
#define FKCONSTR_ACTION_SETDEFAULT
#define FKCONSTR_ACTION_CASCADE
#define FKCONSTR_ACTION_SETNULL
FormData_pg_attribute * Form_pg_attribute
void FindFKPeriodOpers(Oid opclass, Oid *containedbyoperoid, Oid *aggedcontainedbyoperoid, Oid *intersectoperoid)
static void * list_nth(const List *list, int n)
Definition pg_list.h:299
static ListCell * list_head(const List *l)
Definition pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition pg_list.h:343
END_CATALOG_STRUCT typedef FormData_pg_opclass * Form_pg_opclass
Definition pg_opclass.h:87
@ COERCION_IMPLICIT
Definition primnodes.h:747
#define RelationIsPermanent(relation)
Definition rel.h:626
#define InvalidStrategy
Definition stratnum.h:24
TupleDesc oldDesc
Definition tablecmds.c:174
bool rd_islocaltemp
Definition rel.h:61
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:264
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:220
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition table.c:83
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, List **attnamelist, int16 *attnums, Oid *atttypids, Oid *attcollids, Oid *opclasses, bool *pk_has_without_overlaps)
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:160
#define strVal(v)
Definition value.h:82

References addFkBothSides, addFkConstraint(), addFkRecurseReferenced(), addFkRecurseReferencing(), allowSystemTableMods, Assert, can_coerce_type(), checkFkeyPermissions(), COERCION_IMPLICIT, COMPARE_EQ, COMPARE_OVERLAP, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), findFkeyCast(), FindFKPeriodOpers(), FKCONSTR_ACTION_CASCADE, FKCONSTR_ACTION_RESTRICT, FKCONSTR_ACTION_SETDEFAULT, FKCONSTR_ACTION_SETNULL, Form_pg_opclass, format_type_be(), get_am_name(), get_collation_isdeterministic(), get_collation_name(), get_opfamily_member(), get_opfamily_name(), getBaseType(), GETSTRUCT(), HeapTupleIsValid, i, INDEX_MAX_KEYS, IndexAmTranslateCompareType(), InvalidOid, InvalidStrategy, IsSystemRelation(), lfirst_oid, list_head(), list_length(), list_nth(), lnext(), NIL, NoLock, ObjectAddress::objectId, ObjectIdGetDatum(), OidIsValid, AlteredTableInfo::oldDesc, RelationData::rd_islocaltemp, RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RelationIsPermanent, ReleaseSysCache(), SearchSysCache1(), ShareRowExclusiveLock, strVal, table_close(), table_open(), table_openrv(), transformColumnNameList(), transformFkeyCheckAttrs(), transformFkeyGetPrimaryKey(), TupleDescAttr(), and validateFkOnDeleteSetColumns().

Referenced by ATExecAddConstraint().

◆ ATCheckPartitionsNotInUse()

static void ATCheckPartitionsNotInUse ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 6900 of file tablecmds.c.

6901{
6902 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6903 {
6904 List *inh;
6905 ListCell *cell;
6906
6907 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6908 /* first element is the parent rel; must ignore it */
6909 for_each_from(cell, inh, 1)
6910 {
6912
6913 /* find_all_inheritors already got lock */
6917 }
6918 list_free(inh);
6919 }
6920}
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
#define for_each_from(cell, lst, N)
Definition pg_list.h:414

References CheckAlterTableIsSafe(), fb(), 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 14706 of file tablecmds.c.

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

References arg, CoerceToDomain::arg, FuncExpr::args, Assert, DomainHasConstraints(), fb(), 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 4883 of file tablecmds.c.

4886{
4887 List *wqueue = NIL;
4888 ListCell *lcmd;
4889
4890 /* Phase 1: preliminary examination of commands, create work queue */
4891 foreach(lcmd, cmds)
4892 {
4894
4895 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4896 }
4897
4898 /* Close the relation, but keep lock until commit */
4899 relation_close(rel, NoLock);
4900
4901 /* Phase 2: update system catalogs */
4902 ATRewriteCatalogs(&wqueue, lockmode, context);
4903
4904 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4905 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4906}
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:5321
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:4918
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:5873

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

Referenced by AlterTable(), and AlterTableInternal().

◆ ATDetachCheckNoForeignKeyRefs()

static void ATDetachCheckNoForeignKeyRefs ( Relation  partition)
static

Definition at line 21985 of file tablecmds.c.

21986{
21987 List *constraints;
21988 ListCell *cell;
21989
21990 constraints = GetParentedForeignKeyRefs(partition);
21991
21992 foreach(cell, constraints)
21993 {
21994 Oid constrOid = lfirst_oid(cell);
21995 HeapTuple tuple;
21997 Relation rel;
21998 Trigger trig = {0};
21999
22001 if (!HeapTupleIsValid(tuple))
22002 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22004
22005 Assert(OidIsValid(constrForm->conparentid));
22007
22008 /* prevent data changes into the referencing table until commit */
22009 rel = table_open(constrForm->conrelid, ShareLock);
22010
22011 trig.tgoid = InvalidOid;
22012 trig.tgname = NameStr(constrForm->conname);
22013 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
22014 trig.tgisinternal = true;
22015 trig.tgconstrrelid = RelationGetRelid(partition);
22016 trig.tgconstrindid = constrForm->conindid;
22017 trig.tgconstraint = constrForm->oid;
22018 trig.tgdeferrable = false;
22019 trig.tginitdeferred = false;
22020 /* we needn't fill in remaining fields */
22021
22023
22024 ReleaseSysCache(tuple);
22025
22026 table_close(rel, NoLock);
22027 }
22028}
#define ShareLock
Definition lockdefs.h:40
void RI_PartitionRemove_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
static List * GetParentedForeignKeyRefs(Relation partition)
#define TRIGGER_FIRES_ON_ORIGIN
Definition trigger.h:151

References Assert, elog, ERROR, fb(), Form_pg_constraint, GetParentedForeignKeyRefs(), GETSTRUCT(), HeapTupleIsValid, InvalidOid, lfirst_oid, NameStr, NoLock, ObjectIdGetDatum(), OidIsValid, RelationGetRelid, ReleaseSysCache(), RI_PartitionRemove_Check(), SearchSysCache1(), ShareLock, table_close(), table_open(), 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 19484 of file tablecmds.c.

19486{
19488
19489 foreach(cur_item, on_commits)
19490 {
19492
19493 if (!isCommit && oc->creating_subid == mySubid)
19494 {
19495 /* cur_item must be removed */
19497 pfree(oc);
19498 }
19499 else
19500 {
19501 /* cur_item must be preserved */
19502 if (oc->creating_subid == mySubid)
19503 oc->creating_subid = parentSubid;
19504 if (oc->deleting_subid == mySubid)
19505 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19506 }
19507 }
19508}
#define InvalidSubTransactionId
Definition c.h:684
#define foreach_delete_current(lst, var_or_cell)
Definition pg_list.h:391
static List * on_commits
Definition tablecmds.c:132

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

19453{
19455
19456 foreach(cur_item, on_commits)
19457 {
19459
19460 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19461 oc->creating_subid != InvalidSubTransactionId)
19462 {
19463 /* cur_item must be removed */
19465 pfree(oc);
19466 }
19467 else
19468 {
19469 /* cur_item must be preserved */
19470 oc->creating_subid = InvalidSubTransactionId;
19471 oc->deleting_subid = InvalidSubTransactionId;
19472 }
19473 }
19474}

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

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

References add_column_collation_dependency(), add_column_datatype_dependency(), AddRelationNewConstraints(), Assert, AT_REWRITE_DEFAULT_VAL, ATExecAddColumn(), ATGetQueueEntry(), ATParseTransformCmd(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, build_column_default(), BuildDescForRelation(), castNode, CatalogTupleUpdate(), check_for_column_name_collision(), check_stack_depth(), CheckAlterTableIsSafe(), CheckAttributeType(), CHKATYPE_IS_VIRTUAL, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, CommandCounterIncrement(), contain_volatile_functions(), copyObject, CreateExecutorState(), DomainHasConstraints(), elog, ereport, errcode(), errdetail(), errmsg(), ERROR, ExecEvalExpr(), ExecPrepareExpr(), expression_planner(), fb(), find_inheritance_children(), FreeExecutorState(), get_collation_name(), get_typcollation(), getBaseTypeAndTypmod(), GetColumnDefCollation(), GetPerTupleExprContext, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InsertPgAttributeTuples(), InvalidObjectAddress, InvokeObjectPostCreateHook, lappend(), lfirst_oid, list_make1, list_make1_oid, makeNode, makeNullConst(), MaxHeapAttributeNumber, NameStr, newval, AlteredTableInfo::newvals, NIL, NoLock, NOTICE, ObjectAddressSubSet, ObjectIdGetDatum(), palloc0_object, palloc_object, pg_add_s16_overflow(), RangeVarGetRelid, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, AlteredTableInfo::rewrite, RowExclusiveLock, SearchSysCacheCopy1, SearchSysCacheCopyAttName(), StoreAttrMissingVal(), HeapTupleData::t_self, table_close(), table_open(), TupleDescAttr(), typenameTypeIdAndMod(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATExecAddColumn(), and ATExecCmd().

◆ ATExecAddConstraint()

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

Definition at line 9823 of file tablecmds.c.

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

References Assert, ATAddCheckNNConstraint(), ATAddForeignKeyConstraint(), ChooseConstraintName(), ChooseForeignKeyConstraintNameAddition(), CONSTR_CHECK, CONSTR_FOREIGN, CONSTR_NOTNULL, CONSTRAINT_RELATION, ConstraintNameIsUsed(), elog, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, fb(), 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 8279 of file tablecmds.c.

8281{
8283 HeapTuple tuple;
8286 ObjectAddress address;
8288 bool ispartitioned;
8289
8290 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8291 if (ispartitioned && !recurse)
8292 ereport(ERROR,
8294 errmsg("cannot add identity to a column of only the partitioned table"),
8295 errhint("Do not specify the ONLY keyword.")));
8296
8297 if (rel->rd_rel->relispartition && !recursing)
8298 ereport(ERROR,
8300 errmsg("cannot add identity to a column of a partition"));
8301
8303
8305 if (!HeapTupleIsValid(tuple))
8306 ereport(ERROR,
8308 errmsg("column \"%s\" of relation \"%s\" does not exist",
8311 attnum = attTup->attnum;
8312
8313 /* Can't alter a system attribute */
8314 if (attnum <= 0)
8315 ereport(ERROR,
8317 errmsg("cannot alter system column \"%s\"",
8318 colName)));
8319
8320 /*
8321 * Creating a column as identity implies NOT NULL, so adding the identity
8322 * to an existing column that is not NOT NULL would create a state that
8323 * cannot be reproduced without contortions.
8324 */
8325 if (!attTup->attnotnull)
8326 ereport(ERROR,
8328 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8330
8331 /*
8332 * On the other hand, if a not-null constraint exists, then verify that
8333 * it's compatible.
8334 */
8335 if (attTup->attnotnull)
8336 {
8339
8341 attnum);
8343 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8345
8347 if (!conForm->convalidated)
8348 ereport(ERROR,
8350 errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8351 NameStr(conForm->conname), RelationGetRelationName(rel)),
8352 errhint("You might need to validate it using %s.",
8353 "ALTER TABLE ... VALIDATE CONSTRAINT"));
8354 }
8355
8356 if (attTup->attidentity)
8357 ereport(ERROR,
8359 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8361
8362 if (attTup->atthasdef)
8363 ereport(ERROR,
8365 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8367
8368 attTup->attidentity = cdef->identity;
8369 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8370
8372 RelationGetRelid(rel),
8373 attTup->attnum);
8375 RelationGetRelid(rel), attnum);
8376 heap_freetuple(tuple);
8377
8379
8380 /*
8381 * Recurse to propagate the identity column to partitions. Identity is
8382 * not inherited in regular inheritance children.
8383 */
8384 if (recurse && ispartitioned)
8385 {
8386 List *children;
8387 ListCell *lc;
8388
8389 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8390
8391 foreach(lc, children)
8392 {
8394
8396 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8398 }
8399 }
8400
8401 return address;
8402}
int errhint(const char *fmt,...) pg_attribute_printf(1
HeapTuple findNotNullConstraintAttnum(Oid relid, AttrNumber attnum)
static ObjectAddress ATExecAddIdentity(Relation rel, const char *colName, Node *def, LOCKMODE lockmode, bool recurse, bool recursing)
Definition tablecmds.c:8279

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

Referenced by ATExecAddIdentity(), and ATExecCmd().

◆ ATExecAddIndex()

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

Definition at line 9646 of file tablecmds.c.

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

References Assert, DefineIndex(), fb(), 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 9731 of file tablecmds.c.

9733{
9734 Oid index_oid = stmt->indexOid;
9735 Relation indexRel;
9736 char *indexName;
9737 IndexInfo *indexInfo;
9738 char *constraintName;
9739 char constraintType;
9740 ObjectAddress address;
9741 bits16 flags;
9742
9745 Assert(stmt->isconstraint);
9746
9747 /*
9748 * Doing this on partitioned tables is not a simple feature to implement,
9749 * so let's punt for now.
9750 */
9751 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9752 ereport(ERROR,
9754 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9755
9757
9759
9760 indexInfo = BuildIndexInfo(indexRel);
9761
9762 /* this should have been checked at parse time */
9763 if (!indexInfo->ii_Unique)
9764 elog(ERROR, "index \"%s\" is not unique", indexName);
9765
9766 /*
9767 * Determine name to assign to constraint. We require a constraint to
9768 * have the same name as the underlying index; therefore, use the index's
9769 * existing name as the default constraint name, and if the user
9770 * explicitly gives some other name for the constraint, rename the index
9771 * to match.
9772 */
9773 constraintName = stmt->idxname;
9774 if (constraintName == NULL)
9776 else if (strcmp(constraintName, indexName) != 0)
9777 {
9779 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9782 }
9783
9784 /* Extra checks needed if making primary key */
9785 if (stmt->primary)
9786 index_check_primary_key(rel, indexInfo, true, stmt);
9787
9788 /* Note we currently don't support EXCLUSION constraints here */
9789 if (stmt->primary)
9791 else
9793
9794 /* Create the catalog entries for the constraint */
9797 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9798 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9800
9801 address = index_constraint_create(rel,
9802 index_oid,
9803 InvalidOid,
9804 indexInfo,
9807 flags,
9809 false); /* is_internal */
9810
9811 index_close(indexRel, NoLock);
9812
9813 return address;
9814}
uint16 bits16
Definition c.h:566
IndexInfo * BuildIndexInfo(Relation index)
Definition index.c:2426
void index_check_primary_key(Relation heapRel, const IndexInfo *indexInfo, bool is_alter_table, const IndexStmt *stmt)
Definition index.c:202
ObjectAddress index_constraint_create(Relation heapRelation, Oid indexRelationId, Oid parentConstraintId, const IndexInfo *indexInfo, const char *constraintName, char constraintType, bits16 constr_flags, bool allow_system_table_mods, bool is_internal)
Definition index.c:1883
#define INDEX_CONSTR_CREATE_UPDATE_INDEX
Definition index.h:94
#define INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
Definition index.h:95
#define INDEX_CONSTR_CREATE_DEFERRABLE
Definition index.h:92
#define INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
Definition index.h:91
#define INDEX_CONSTR_CREATE_INIT_DEFERRED
Definition index.h:93
bool ii_Unique
Definition execnodes.h:202
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition tablecmds.c:4278

References AccessShareLock, allowSystemTableMods, Assert, BuildIndexInfo(), elog, ereport, errcode(), errmsg(), ERROR, fb(), 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 17288 of file tablecmds.c.

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

References AccessShareLock, AT_AddInherit, ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, CreateInheritance(), ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), find_all_inheritors(), FindTriggerIncompatibleWithInheritance(), list_member_oid(), NoLock, ObjectAddressSet, RELATION_IS_OTHER_TEMP, RelationGetRelationName, RelationGetRelid, RangeVar::relname, ShareUpdateExclusiveLock, table_close(), and table_openrv().

Referenced by ATExecCmd().

◆ ATExecAddOf()

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

Definition at line 18241 of file tablecmds.c.

18242{
18243 Oid relid = RelationGetRelid(rel);
18246 Oid typeid;
18249 SysScanDesc scan;
18252 type_attno;
18256 typeobj;
18258
18259 /* Validate the type. */
18260 typetuple = typenameType(NULL, ofTypename, NULL);
18263 typeid = typeform->oid;
18264
18265 /* Fail if the table has any inheritance parents. */
18267 ScanKeyInit(&key,
18270 ObjectIdGetDatum(relid));
18272 true, NULL, 1, &key);
18274 ereport(ERROR,
18276 errmsg("typed tables cannot inherit")));
18277 systable_endscan(scan);
18279
18280 /*
18281 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18282 * require that the order also match. However, attnotnull need not match.
18283 */
18286 table_attno = 1;
18287 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18288 {
18290 table_attr;
18291 const char *type_attname,
18293
18294 /* Get the next non-dropped type attribute. */
18296 if (type_attr->attisdropped)
18297 continue;
18298 type_attname = NameStr(type_attr->attname);
18299
18300 /* Get the next non-dropped table attribute. */
18301 do
18302 {
18303 if (table_attno > tableTupleDesc->natts)
18304 ereport(ERROR,
18306 errmsg("table is missing column \"%s\"",
18307 type_attname)));
18309 table_attno++;
18310 } while (table_attr->attisdropped);
18311 table_attname = NameStr(table_attr->attname);
18312
18313 /* Compare name. */
18315 ereport(ERROR,
18317 errmsg("table has column \"%s\" where type requires \"%s\"",
18319
18320 /* Compare type. */
18321 if (table_attr->atttypid != type_attr->atttypid ||
18322 table_attr->atttypmod != type_attr->atttypmod ||
18323 table_attr->attcollation != type_attr->attcollation)
18324 ereport(ERROR,
18326 errmsg("table \"%s\" has different type for column \"%s\"",
18328 }
18330
18331 /* Any remaining columns at the end of the table had better be dropped. */
18332 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18333 {
18335 table_attno - 1);
18336
18337 if (!table_attr->attisdropped)
18338 ereport(ERROR,
18340 errmsg("table has extra column \"%s\"",
18341 NameStr(table_attr->attname))));
18342 }
18343
18344 /* If the table was already typed, drop the existing dependency. */
18345 if (rel->rd_rel->reloftype)
18346 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18348
18349 /* Record a dependency on the new type. */
18350 tableobj.classId = RelationRelationId;
18351 tableobj.objectId = relid;
18352 tableobj.objectSubId = 0;
18353 typeobj.classId = TypeRelationId;
18354 typeobj.objectId = typeid;
18355 typeobj.objectSubId = 0;
18357
18358 /* Update pg_class.reloftype */
18362 elog(ERROR, "cache lookup failed for relation %u", relid);
18363 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18365
18367
18370
18372
18373 return typeobj;
18374}
Type typenameType(ParseState *pstate, const TypeName *typeName, int32 *typmod_p)
Definition parse_type.c:264
#define NAMEDATALEN
END_CATALOG_STRUCT typedef FormData_pg_type * Form_pg_type
Definition pg_type.h:265
static void drop_parent_dependency(Oid relid, Oid refclassid, Oid refobjid, DependencyType deptype)
void check_of_type(HeapTuple typetuple)
Definition tablecmds.c:7182
#define ReleaseTupleDesc(tupdesc)
Definition tupdesc.h:219
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition typcache.c:1924

References AccessShareLock, BTEqualStrategyNumber, CatalogTupleUpdate(), check_of_type(), DEPENDENCY_NORMAL, drop_parent_dependency(), elog, ereport, errcode(), errmsg(), ERROR, fb(), Form_pg_type, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, lookup_rowtype_tupdesc(), NAMEDATALEN, NameStr, ObjectIdGetDatum(), RelationData::rd_rel, recordDependencyOn(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), ReleaseTupleDesc, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopy1, systable_beginscan(), systable_endscan(), systable_getnext(), 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 9710 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecAlterColumnGenericOptions()

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

Definition at line 15982 of file tablecmds.c.

15986{
15989 ForeignServer *server;
15991 HeapTuple tuple;
15992 HeapTuple newtuple;
15993 bool isnull;
15997 Datum datum;
16001 ObjectAddress address;
16002
16003 if (options == NIL)
16004 return InvalidObjectAddress;
16005
16006 /* First, determine FDW validator associated to the foreign table. */
16009 if (!HeapTupleIsValid(tuple))
16010 ereport(ERROR,
16012 errmsg("foreign table \"%s\" does not exist",
16015 server = GetForeignServer(fttableform->ftserver);
16016 fdw = GetForeignDataWrapper(server->fdwid);
16017
16019 ReleaseSysCache(tuple);
16020
16023 if (!HeapTupleIsValid(tuple))
16024 ereport(ERROR,
16026 errmsg("column \"%s\" of relation \"%s\" does not exist",
16028
16029 /* Prevent them from altering a system attribute */
16031 attnum = atttableform->attnum;
16032 if (attnum <= 0)
16033 ereport(ERROR,
16035 errmsg("cannot alter system column \"%s\"", colName)));
16036
16037
16038 /* Initialize buffers for new tuple values */
16039 memset(repl_val, 0, sizeof(repl_val));
16040 memset(repl_null, false, sizeof(repl_null));
16041 memset(repl_repl, false, sizeof(repl_repl));
16042
16043 /* Extract the current options */
16044 datum = SysCacheGetAttr(ATTNAME,
16045 tuple,
16047 &isnull);
16048 if (isnull)
16049 datum = PointerGetDatum(NULL);
16050
16051 /* Transform the options */
16053 datum,
16054 options,
16055 fdw->fdwvalidator);
16056
16057 if (DatumGetPointer(datum) != NULL)
16059 else
16061
16063
16064 /* Everything looks good - update the tuple */
16065
16066 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16068
16069 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16070
16072 RelationGetRelid(rel),
16073 atttableform->attnum);
16075 RelationGetRelid(rel), attnum);
16076
16077 ReleaseSysCache(tuple);
16078
16080
16081 heap_freetuple(newtuple);
16082
16083 return address;
16084}
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition foreign.c:38
ForeignServer * GetForeignServer(Oid serverid)
Definition foreign.c:112
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition heaptuple.c:1210
END_CATALOG_STRUCT typedef FormData_pg_foreign_table * Form_pg_foreign_table
static Datum PointerGetDatum(const void *X)
Definition postgres.h:352
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:342
Oid rd_id
Definition rel.h:113
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition syscache.c:475
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:595

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

Referenced by ATExecCmd().

◆ ATExecAlterColumnType()

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

Definition at line 14753 of file tablecmds.c.

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

References add_column_collation_dependency(), add_column_datatype_dependency(), array_get_element(), TypeName::arrayBounds, 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(), fb(), Form_pg_depend, Form_pg_type, format_type_be(), GetAttrDefaultOid(), GetColumnDefCollation(), getObjectDescription(), GETSTRUCT(), heap_freetuple(), heap_getattr(), heap_modify_tuple(), HeapTupleIsValid, Int32GetDatum(), InvalidCompressionMethod, InvokeObjectPostAlterHook, list_length(), AlterTableCmd::name, NoLock, ObjectAddressSubSet, ObjectIdGetDatum(), OidIsValid, AlteredTableInfo::oldDesc, PG_INT16_MAX, PointerGetDatum(), relation_close(), RelationClearMissing(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RememberAllDependentForRebuilding(), RemoveAttrDefault(), RemoveStatistics(), AlteredTableInfo::rewrite, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopyAttName(), StoreAttrDefault(), strip_implicit_coercions(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TupleDescAttr(), ColumnDef::typeName, and typenameType().

Referenced by ATExecCmd().

◆ ATExecAlterConstraint()

static ObjectAddress ATExecAlterConstraint ( List **  wqueue,
Relation  rel,
ATAlterConstraint cmdcon,
bool  recurse,
LOCKMODE  lockmode 
)
static

Definition at line 12225 of file tablecmds.c.

12227{
12230 SysScanDesc scan;
12231 ScanKeyData skey[3];
12234 ObjectAddress address;
12235
12236 /*
12237 * Disallow altering ONLY a partitioned table, as it would make no sense.
12238 * This is okay for legacy inheritance.
12239 */
12240 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12241 ereport(ERROR,
12243 errmsg("constraint must be altered in child tables too"),
12244 errhint("Do not specify the ONLY keyword."));
12245
12246
12249
12250 /*
12251 * Find and check the target constraint
12252 */
12253 ScanKeyInit(&skey[0],
12257 ScanKeyInit(&skey[1],
12261 ScanKeyInit(&skey[2],
12264 CStringGetDatum(cmdcon->conname));
12266 true, NULL, 3, skey);
12267
12268 /* There can be at most one matching row */
12270 ereport(ERROR,
12272 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12273 cmdcon->conname, RelationGetRelationName(rel))));
12274
12276 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12277 ereport(ERROR,
12279 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12280 cmdcon->conname, RelationGetRelationName(rel))));
12281 if (cmdcon->alterEnforceability && currcon->contype != CONSTRAINT_FOREIGN)
12282 ereport(ERROR,
12284 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12285 cmdcon->conname, RelationGetRelationName(rel))));
12286 if (cmdcon->alterInheritability &&
12287 currcon->contype != CONSTRAINT_NOTNULL)
12288 ereport(ERROR,
12290 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12291 cmdcon->conname, RelationGetRelationName(rel)));
12292
12293 /* Refuse to modify inheritability of inherited constraints */
12294 if (cmdcon->alterInheritability &&
12295 cmdcon->noinherit && currcon->coninhcount > 0)
12296 ereport(ERROR,
12298 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12299 NameStr(currcon->conname),
12301
12302 /*
12303 * If it's not the topmost constraint, raise an error.
12304 *
12305 * Altering a non-topmost constraint leaves some triggers untouched, since
12306 * they are not directly connected to this constraint; also, pg_dump would
12307 * ignore the deferrability status of the individual constraint, since it
12308 * only dumps topmost constraints. Avoid these problems by refusing this
12309 * operation and telling the user to alter the parent constraint instead.
12310 */
12311 if (OidIsValid(currcon->conparentid))
12312 {
12313 HeapTuple tp;
12314 Oid parent = currcon->conparentid;
12315 char *ancestorname = NULL;
12316 char *ancestortable = NULL;
12317
12318 /* Loop to find the topmost constraint */
12320 {
12322
12323 /* If no parent, this is the constraint we want */
12324 if (!OidIsValid(contup->conparentid))
12325 {
12326 ancestorname = pstrdup(NameStr(contup->conname));
12327 ancestortable = get_rel_name(contup->conrelid);
12328 ReleaseSysCache(tp);
12329 break;
12330 }
12331
12332 parent = contup->conparentid;
12333 ReleaseSysCache(tp);
12334 }
12335
12336 ereport(ERROR,
12338 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12339 cmdcon->conname, RelationGetRelationName(rel)),
12341 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12342 cmdcon->conname, ancestorname, ancestortable) : 0,
12343 errhint("You may alter the constraint it derives from instead.")));
12344 }
12345
12346 address = InvalidObjectAddress;
12347
12348 /*
12349 * Do the actual catalog work, and recurse if necessary.
12350 */
12352 contuple, recurse, lockmode))
12354
12355 systable_endscan(scan);
12356
12359
12360 return address;
12361}
static Datum CStringGetDatum(const char *X)
Definition postgres.h:380
static bool ATExecAlterConstraintInternal(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)

References ATExecAlterConstraintInternal(), BTEqualStrategyNumber, CStringGetDatum(), ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, fb(), Form_pg_constraint, get_rel_name(), GETSTRUCT(), HeapTupleIsValid, InvalidObjectAddress, InvalidOid, NameStr, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, pstrdup(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, ScanKeyInit(), SearchSysCache1(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecAlterConstraintInternal()

static bool ATExecAlterConstraintInternal ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  tgrel,
Relation  rel,
HeapTuple  contuple,
bool  recurse,
LOCKMODE  lockmode 
)
static

Definition at line 12368 of file tablecmds.c.

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

References ATExecAlterConstrDeferrability(), ATExecAlterConstrEnforceability(), ATExecAlterConstrInheritability(), CacheInvalidateRelcacheByRelid(), fb(), foreach_oid, Form_pg_constraint, GETSTRUCT(), InvalidOid, and NIL.

Referenced by ATExecAlterConstraint().

◆ ATExecAlterConstrDeferrability()

static bool ATExecAlterConstrDeferrability ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  tgrel,
Relation  rel,
HeapTuple  contuple,
bool  recurse,
List **  otherrelids,
LOCKMODE  lockmode 
)
static

Definition at line 12585 of file tablecmds.c.

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

References AlterConstrDeferrabilityRecurse(), AlterConstrTriggerDeferrability(), AlterConstrUpdateConstraintEntry(), Assert, check_stack_depth(), fb(), Form_pg_constraint, get_rel_relkind(), GETSTRUCT(), and RelationData::rd_rel.

Referenced by AlterConstrDeferrabilityRecurse(), and ATExecAlterConstraintInternal().

◆ ATExecAlterConstrEnforceability()

static bool ATExecAlterConstrEnforceability ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  tgrel,
Oid  fkrelid,
Oid  pkrelid,
HeapTuple  contuple,
LOCKMODE  lockmode,
Oid  ReferencedParentDelTrigger,
Oid  ReferencedParentUpdTrigger,
Oid  ReferencingParentInsTrigger,
Oid  ReferencingParentUpdTrigger 
)
static

Definition at line 12439 of file tablecmds.c.

12447{
12449 Oid conoid;
12450 Relation rel;
12451 bool changed = false;
12452
12453 /* Since this function recurses, it could be driven to stack overflow */
12455
12456 Assert(cmdcon->alterEnforceability);
12457
12459 conoid = currcon->oid;
12460
12461 /* Should be foreign key constraint */
12462 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12463
12464 rel = table_open(currcon->conrelid, lockmode);
12465
12466 if (currcon->conenforced != cmdcon->is_enforced)
12467 {
12469 changed = true;
12470 }
12471
12472 /* Drop triggers */
12473 if (!cmdcon->is_enforced)
12474 {
12475 /*
12476 * When setting a constraint to NOT ENFORCED, the constraint triggers
12477 * need to be dropped. Therefore, we must process the child relations
12478 * first, followed by the parent, to account for dependencies.
12479 */
12480 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12484 lockmode, InvalidOid, InvalidOid,
12486
12487 /* Drop all the triggers */
12489 }
12490 else if (changed) /* Create triggers */
12491 {
12496
12497 /* Prepare the minimal information required for trigger creation. */
12499
12500 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12501 fkconstraint->fk_matchtype = currcon->confmatchtype;
12502 fkconstraint->fk_upd_action = currcon->confupdtype;
12503 fkconstraint->fk_del_action = currcon->confdeltype;
12504
12505 /* Create referenced triggers */
12506 if (currcon->conrelid == fkrelid)
12508 currcon->confrelid,
12510 conoid,
12511 currcon->conindid,
12516
12517 /* Create referencing triggers */
12518 if (currcon->confrelid == pkrelid)
12520 pkrelid,
12522 conoid,
12523 currcon->conindid,
12528
12529 /*
12530 * Tell Phase 3 to check that the constraint is satisfied by existing
12531 * rows. Only applies to leaf partitions, and (for constraints that
12532 * reference a partitioned table) only if this is not one of the
12533 * pg_constraint rows that exist solely to support action triggers.
12534 */
12535 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12536 currcon->confrelid == pkrelid)
12537 {
12538 AlteredTableInfo *tab;
12540
12542 newcon->name = fkconstraint->conname;
12543 newcon->contype = CONSTR_FOREIGN;
12544 newcon->refrelid = currcon->confrelid;
12545 newcon->refindid = currcon->conindid;
12546 newcon->conid = currcon->oid;
12547 newcon->qual = (Node *) fkconstraint;
12548
12549 /* Find or create work queue entry for this table */
12550 tab = ATGetQueueEntry(wqueue, rel);
12551 tab->constraints = lappend(tab->constraints, newcon);
12552 }
12553
12554 /*
12555 * If the table at either end of the constraint is partitioned, we
12556 * need to recurse and create triggers for each constraint that is a
12557 * child of this one.
12558 */
12559 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12563 lockmode, ReferencedDelTriggerOid,
12567 }
12568
12569 table_close(rel, NoLock);
12570
12571 return changed;
12572}
static void AlterConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)

References AlterConstrEnforceabilityRecurse(), AlterConstrUpdateConstraintEntry(), Assert, ATGetQueueEntry(), check_stack_depth(), CONSTR_FOREIGN, AlteredTableInfo::constraints, createForeignKeyActionTriggers(), createForeignKeyCheckTriggers(), DropForeignKeyConstraintTriggers(), fb(), Form_pg_constraint, get_rel_relkind(), GETSTRUCT(), InvalidOid, lappend(), makeNode, NameStr, NoLock, palloc0_object, pstrdup(), RelationData::rd_rel, table_close(), and table_open().

Referenced by AlterConstrEnforceabilityRecurse(), and ATExecAlterConstraintInternal().

◆ ATExecAlterConstrInheritability()

static bool ATExecAlterConstrInheritability ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Relation  rel,
HeapTuple  contuple,
LOCKMODE  lockmode 
)
static

Definition at line 12642 of file tablecmds.c.

12645{
12648 char *colName;
12649 List *children;
12650
12651 Assert(cmdcon->alterInheritability);
12652
12654
12655 /* The current implementation only works for NOT NULL constraints */
12656 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12657
12658 /*
12659 * If called to modify a constraint that's already in the desired state,
12660 * silently do nothing.
12661 */
12662 if (cmdcon->noinherit == currcon->connoinherit)
12663 return false;
12664
12667
12668 /* Fetch the column number and name */
12670 colName = get_attname(currcon->conrelid, colNum, false);
12671
12672 /*
12673 * Propagate the change to children. For this subcommand type we don't
12674 * recursively affect children, just the immediate level.
12675 */
12677 lockmode);
12678 foreach_oid(childoid, children)
12679 {
12680 ObjectAddress addr;
12681
12682 if (cmdcon->noinherit)
12683 {
12686
12688 if (!childtup)
12689 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12690 colName, childoid);
12692 Assert(childcon->coninhcount > 0);
12693 childcon->coninhcount--;
12694 childcon->conislocal = true;
12697 }
12698 else
12699 {
12701
12702 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12703 colName, true, true, lockmode);
12704 if (OidIsValid(addr.objectId))
12707 }
12708 }
12709
12710 return true;
12711}
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition lsyscache.c:903
HeapTuple findNotNullConstraint(Oid relid, const char *colname)
AttrNumber extractNotNullColumn(HeapTuple constrTup)
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition tablecmds.c:7952

References AlterConstrUpdateConstraintEntry(), Assert, ATExecSetNotNull(), CatalogTupleUpdate(), CommandCounterIncrement(), elog, ERROR, extractNotNullColumn(), fb(), find_inheritance_children(), findNotNullConstraint(), foreach_oid, Form_pg_constraint, get_attname(), GETSTRUCT(), heap_freetuple(), NameStr, NoLock, ObjectAddress::objectId, OidIsValid, RelationGetRelid, table_close(), and table_open().

Referenced by ATExecAlterConstraintInternal().

◆ ATExecAttachPartition()

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

Definition at line 20309 of file tablecmds.c.

20311{
20313 catalog;
20316 SysScanDesc scan;
20318 AttrNumber attno;
20319 int natts;
20321 ObjectAddress address;
20322 const char *trigger_name;
20325 ParseState *pstate = make_parsestate(NULL);
20326
20327 pstate->p_sourcetext = context->queryString;
20328
20329 /*
20330 * We must lock the default partition if one exists, because attaching a
20331 * new partition will change its partition constraint.
20332 */
20337
20339
20340 /*
20341 * XXX I think it'd be a good idea to grab locks on all tables referenced
20342 * by FKs at this point also.
20343 */
20344
20345 /*
20346 * Must be owner of both parent and source table -- parent was checked by
20347 * ATSimplePermissions call in ATPrepCmd
20348 */
20351
20352 /* A partition can only have one parent */
20353 if (attachrel->rd_rel->relispartition)
20354 ereport(ERROR,
20356 errmsg("\"%s\" is already a partition",
20358
20359 if (OidIsValid(attachrel->rd_rel->reloftype))
20360 ereport(ERROR,
20362 errmsg("cannot attach a typed table as partition")));
20363
20364 /*
20365 * Table being attached should not already be part of inheritance; either
20366 * as a child table...
20367 */
20374 NULL, 1, &skey);
20376 ereport(ERROR,
20378 errmsg("cannot attach inheritance child as partition")));
20379 systable_endscan(scan);
20380
20381 /* ...or as a parent table (except the case when it is partitioned) */
20387 1, &skey);
20389 attachrel->rd_rel->relkind == RELKIND_RELATION)
20390 ereport(ERROR,
20392 errmsg("cannot attach inheritance parent as partition")));
20393 systable_endscan(scan);
20395
20396 /*
20397 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20398 * particular, this disallows making a rel a partition of itself.)
20399 *
20400 * We do that by checking if rel is a member of the list of attachrel's
20401 * partitions provided the latter is partitioned at all. We want to avoid
20402 * having to construct this list again, so we request the strongest lock
20403 * on all partitions. We need the strongest lock, because we may decide
20404 * to scan them if we find out that the table being attached (or its leaf
20405 * partitions) may contain rows that violate the partition constraint. If
20406 * the table has a constraint that would prevent such rows, which by
20407 * definition is present in all the partitions, we need not scan the
20408 * table, nor its partitions. But we cannot risk a deadlock by taking a
20409 * weaker lock now and the stronger one only when needed.
20410 */
20414 ereport(ERROR,
20416 errmsg("circular inheritance not allowed"),
20417 errdetail("\"%s\" is already a child of \"%s\".",
20420
20421 /* If the parent is permanent, so must be all of its partitions. */
20422 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20423 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20424 ereport(ERROR,
20426 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20428
20429 /* Temp parent cannot have a partition that is itself not a temp */
20430 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20431 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20432 ereport(ERROR,
20434 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20436
20437 /* If the parent is temp, it must belong to this session */
20438 if (RELATION_IS_OTHER_TEMP(rel))
20439 ereport(ERROR,
20441 errmsg("cannot attach as partition of temporary relation of another session")));
20442
20443 /* Ditto for the partition */
20445 ereport(ERROR,
20447 errmsg("cannot attach temporary relation of another session as partition")));
20448
20449 /*
20450 * Check if attachrel has any identity columns or any columns that aren't
20451 * in the parent.
20452 */
20454 natts = tupleDesc->natts;
20455 for (attno = 1; attno <= natts; attno++)
20456 {
20457 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20458 char *attributeName = NameStr(attribute->attname);
20459
20460 /* Ignore dropped */
20461 if (attribute->attisdropped)
20462 continue;
20463
20464 if (attribute->attidentity)
20465 ereport(ERROR,
20467 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20469 errdetail("The new partition may not contain an identity column."));
20470
20471 /* Try to find the column in parent (matching on column name) */
20475 ereport(ERROR,
20477 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20480 errdetail("The new partition may contain only the columns present in parent.")));
20481 }
20482
20483 /*
20484 * If child_rel has row-level triggers with transition tables, we
20485 * currently don't allow it to become a partition. See also prohibitions
20486 * in ATExecAddInherit() and CreateTrigger().
20487 */
20489 if (trigger_name != NULL)
20490 ereport(ERROR,
20492 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20494 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20495
20496 /*
20497 * Check that the new partition's bound is valid and does not overlap any
20498 * of existing partitions of the parent - note that it does not return on
20499 * error.
20500 */
20502 cmd->bound, pstate);
20503
20505
20506 /*
20507 * Generate a partition constraint from the partition bound specification.
20508 * If the parent itself is a partition, make sure to include its
20509 * constraint as well.
20510 */
20512
20513 /*
20514 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20515 * since it's needed later to construct the constraint expression for
20516 * validating against the default partition, if any.
20517 */
20520
20521 /* Skip validation if there are no constraints to validate. */
20522 if (partConstraint)
20523 {
20524 /*
20525 * Run the partition quals through const-simplification similar to
20526 * check constraints. We skip canonicalize_qual, though, because
20527 * partition quals should be in canonical form already.
20528 */
20531 (Node *) partConstraint);
20532
20533 /* XXX this sure looks wrong */
20535
20536 /*
20537 * Adjust the generated constraint to match this partition's attribute
20538 * numbers.
20539 */
20541 rel);
20542
20543 /* Validate partition constraints against the table being attached. */
20545 false);
20546 }
20547
20548 /*
20549 * If we're attaching a partition other than the default partition and a
20550 * default one exists, then that partition's partition constraint changes,
20551 * so add an entry to the work queue to validate it, too. (We must not do
20552 * this when the partition being attached is the default one; we already
20553 * did it above!)
20554 */
20556 {
20559
20560 Assert(!cmd->bound->is_default);
20561
20562 /* we already hold a lock on the default partition */
20566
20567 /*
20568 * Map the Vars in the constraint expression from rel's attnos to
20569 * defaultrel's.
20570 */
20573 1, defaultrel, rel);
20575 defPartConstraint, true);
20576
20577 /* keep our lock until commit. */
20579 }
20580
20582
20583 /*
20584 * If the partition we just attached is partitioned itself, invalidate
20585 * relcache for all descendent partitions too to ensure that their
20586 * rd_partcheck expression trees are rebuilt; partitions already locked at
20587 * the beginning of this function.
20588 */
20589 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20590 {
20591 ListCell *l;
20592
20593 foreach(l, attachrel_children)
20594 {
20596 }
20597 }
20598
20599 /* keep our lock until commit */
20601
20602 return address;
20603}
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition clauses.c:2267
List * list_concat_copy(const List *list1, const List *list2)
Definition list.c:598
Expr * make_ands_explicit(List *andclauses)
Definition makefuncs.c:799
ParseState * make_parsestate(ParseState *parentParseState)
Definition parse_node.c:39
void check_new_partition_bound(char *relname, Relation parent, PartitionBoundSpec *spec, ParseState *pstate)
List * get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
Definition partbounds.c:250
List * RelationGetPartitionQual(Relation rel)
Definition partcache.c:277
Oid get_default_oid_from_partdesc(PartitionDesc partdesc)
Definition partdesc.c:501
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition partition.c:222
List * get_proposed_default_constraint(List *new_part_constraints)
Definition partition.c:370
const char * queryString
Definition utility.h:33
const char * p_sourcetext
Definition parse_node.h:191
PartitionBoundSpec * bound
Definition parsenodes.h:993
RangeVar * name
Definition parsenodes.h:990
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition syscache.h:102
static void attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)

References AccessExclusiveLock, AccessShareLock, Assert, AT_AttachPartition, ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, attachPartitionTable(), PartitionCmd::bound, BTEqualStrategyNumber, CacheInvalidateRelcacheByRelid(), check_new_partition_bound(), CStringGetDatum(), ereport, errcode(), errdetail(), errmsg(), ERROR, eval_const_expressions(), fb(), find_all_inheritors(), FindTriggerIncompatibleWithInheritance(), get_default_oid_from_partdesc(), get_proposed_default_constraint(), get_qual_from_partbound(), HeapTupleIsValid, PartitionBoundSpec::is_default, lfirst_oid, list_concat_copy(), list_make1, list_member_oid(), LockRelationOid(), make_ands_explicit(), make_parsestate(), map_partition_varattnos(), PartitionCmd::name, NameStr, NoLock, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, ParseState::p_sourcetext, AlterTableUtilityContext::queryString, QueuePartitionConstraintValidation(), RelationData::rd_rel, RELATION_IS_OTHER_TEMP, RelationGetDescr, RelationGetPartitionDesc(), RelationGetPartitionQual(), RelationGetRelationName, RelationGetRelid, ScanKeyInit(), SearchSysCacheExists2, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), table_openrv(), and TupleDescAttr().

Referenced by ATExecCmd().

◆ ATExecAttachPartitionIdx()

static ObjectAddress ATExecAttachPartitionIdx ( List **  wqueue,
Relation  parentIdx,
RangeVar name 
)
static

Definition at line 21622 of file tablecmds.c.

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

References AccessExclusiveLock, AccessShareLock, build_attrmap_by_name(), BuildIndexInfo(), CompareIndexInfo(), ConstraintSetParentConstraint(), ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), free_attrmap(), get_partition_parent(), get_relation_idx_constraint_oid(), i, IndexSetParentIndex(), InvalidOid, name, NoLock, ObjectAddressSet, OidIsValid, RangeVarCallbackForAttachIndex(), RangeVarGetRelidExtended(), RelationData::rd_indcollation, RelationData::rd_index, RelationData::rd_opfamily, refuseDupeIndexAttach(), relation_close(), relation_open(), RelationGetDescr, RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, validatePartitionedIndex(), and verifyPartitionIndexNotNull().

Referenced by ATExecCmd().

◆ ATExecChangeOwner()

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

Definition at line 16099 of file tablecmds.c.

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

References ACL_CREATE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, aclnewowner(), AlterTypeOwnerInternal(), ATExecChangeOwner(), CatalogTupleUpdate(), change_owner_fix_column_acls(), change_owner_recurse_to_sequences(), changeDependencyOnOwner(), check_can_set_role(), DatumGetAclP, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, elog, ereport, errcode(), errdetail(), errdetail_relkind_not_supported(), errhint(), errmsg(), ERROR, fb(), 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, pg_fallthrough, PointerGetDatum(), relation_close(), relation_open(), RelationGetDescr, RelationGetIndexList(), RelationGetRelationName, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), sequenceIsOwned(), superuser(), SysCacheGetAttr(), HeapTupleData::t_self, table_close(), table_open(), and WARNING.

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

◆ ATExecClusterOn()

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

Definition at line 16474 of file tablecmds.c.

16475{
16476 Oid indexOid;
16477 ObjectAddress address;
16478
16479 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16480
16481 if (!OidIsValid(indexOid))
16482 ereport(ERROR,
16484 errmsg("index \"%s\" for table \"%s\" does not exist",
16486
16487 /* Check index is valid to cluster on */
16488 check_index_is_clusterable(rel, indexOid, lockmode);
16489
16490 /* And do the work */
16491 mark_index_clustered(rel, indexOid, false);
16492
16493 ObjectAddressSet(address,
16494 RelationRelationId, indexOid);
16495
16496 return address;
16497}
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition cluster.c:494
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition cluster.c:554

References check_index_is_clusterable(), ereport, errcode(), errmsg(), ERROR, fb(), 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 5395 of file tablecmds.c.

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

References AlterDomainAddConstraint(), Assert, AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AddInherit, AT_AddOf, AT_AlterColumnGenericOptions, AT_AlterColumnType, AT_AlterConstraint, AT_AttachPartition, AT_ChangeOwner, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_MergePartitions, AT_NoForceRowSecurity, AT_PASS_ADD_CONSTR, AT_ReAddComment, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReAddIndex, AT_ReAddStatistics, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, AT_SplitPartition, AT_ValidateConstraint, ATExecAddColumn(), ATExecAddConstraint(), ATExecAddIdentity(), ATExecAddIndex(), ATExecAddIndexConstraint(), ATExecAddInherit(), ATExecAddOf(), ATExecAddStatistics(), ATExecAlterColumnGenericOptions(), ATExecAlterColumnType(), ATExecAlterConstraint(), ATExecAttachPartition(), ATExecAttachPartitionIdx(), ATExecChangeOwner(), ATExecClusterOn(), ATExecColumnDefault(), ATExecCookedColumnDefault(), ATExecDetachPartition(), ATExecDetachPartitionFinalize(), ATExecDropCluster(), ATExecDropColumn(), ATExecDropConstraint(), ATExecDropExpression(), ATExecDropIdentity(), ATExecDropInherit(), ATExecDropNotNull(), ATExecDropOf(), ATExecEnableDisableRule(), ATExecEnableDisableTrigger(), ATExecForceNoForceRowSecurity(), ATExecGenericOptions(), ATExecMergePartitions(), ATExecReplicaIdentity(), ATExecSetAccessMethodNoStorage(), ATExecSetCompression(), ATExecSetExpression(), ATExecSetIdentity(), ATExecSetNotNull(), ATExecSetOptions(), ATExecSetRelOptions(), ATExecSetRowSecurity(), ATExecSetStatistics(), ATExecSetStorage(), ATExecSetTableSpaceNoStorage(), ATExecSplitPartition(), ATExecValidateConstraint(), ATParseTransformCmd(), AlterTableCmd::behavior, castNode, AlteredTableInfo::chgAccessMethod, CommandCounterIncrement(), CommentObject(), AlterTableCmd::def, elog, ERROR, EventTriggerCollectAlterTableSubcmd(), fb(), 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 8165 of file tablecmds.c.

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

References AddRelationNewConstraints(), attnum, DROP_RESTRICT, ereport, errcode(), errhint(), errmsg(), ERROR, fb(), get_attnum(), InvalidAttrNumber, list_make1, NIL, ObjectAddressSubSet, palloc_object, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RemoveAttrDefault(), and TupleDescAttr().

Referenced by ATExecCmd().

◆ ATExecCookedColumnDefault()

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

Definition at line 8250 of file tablecmds.c.

8252{
8253 ObjectAddress address;
8254
8255 /* We assume no checking is required */
8256
8257 /*
8258 * Remove any old default for the column. We use RESTRICT here for
8259 * safety, but at present we do not expect anything to depend on the
8260 * default. (In ordinary cases, there could not be a default in place
8261 * anyway, but it's possible when combining LIKE with inheritance.)
8262 */
8264 true);
8265
8266 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8267
8269 RelationGetRelid(rel), attnum);
8270 return address;
8271}

References attnum, DROP_RESTRICT, fb(), ObjectAddressSubSet, RelationGetRelid, RemoveAttrDefault(), and StoreAttrDefault().

Referenced by ATExecCmd().

◆ ATExecDetachPartition()

static ObjectAddress ATExecDetachPartition ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
RangeVar name,
bool  concurrent 
)
static

Definition at line 20954 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecDetachPartitionFinalize()

static ObjectAddress ATExecDetachPartitionFinalize ( Relation  rel,
RangeVar name 
)
static

Definition at line 21461 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecDropCluster()

static void ATExecDropCluster ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 16506 of file tablecmds.c.

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

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

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

References add_exact_object_address(), Assert, AT_DropColumn, ATExecDropColumn(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, attnum, bms_make_singleton(), CatalogTupleUpdate(), check_stack_depth(), CheckAlterTableIsSafe(), CommandCounterIncrement(), elog, ereport, errcode(), errhint(), errmsg(), ERROR, fb(), find_inheritance_children(), FirstLowInvalidHeapAttributeNumber, free_object_addresses(), GETSTRUCT(), has_partition_attrs(), heap_freetuple(), HeapTupleIsValid, InvalidObjectAddress, lfirst_oid, new_object_addresses(), NoLock, NOTICE, performMultipleDeletions(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCacheAttName(), SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd(), and ATExecDropColumn().

◆ ATExecDropConstraint()

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

Definition at line 14039 of file tablecmds.c.

14042{
14044 SysScanDesc scan;
14045 ScanKeyData skey[3];
14046 HeapTuple tuple;
14047 bool found = false;
14048
14050
14051 /*
14052 * Find and drop the target constraint
14053 */
14054 ScanKeyInit(&skey[0],
14058 ScanKeyInit(&skey[1],
14062 ScanKeyInit(&skey[2],
14067 true, NULL, 3, skey);
14068
14069 /* There can be at most one matching row */
14070 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14071 {
14072 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14073 missing_ok, lockmode);
14074 found = true;
14075 }
14076
14077 systable_endscan(scan);
14078
14079 if (!found)
14080 {
14081 if (!missing_ok)
14082 ereport(ERROR,
14084 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14086 else
14088 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14090 }
14091
14093}
static ObjectAddress dropconstraint_internal(Relation rel, HeapTuple constraintTup, DropBehavior behavior, bool recurse, bool recursing, bool missing_ok, LOCKMODE lockmode)

References BTEqualStrategyNumber, CStringGetDatum(), dropconstraint_internal(), ereport, errcode(), errmsg(), ERROR, fb(), HeapTupleIsValid, InvalidOid, NOTICE, ObjectIdGetDatum(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecCmd().

◆ ATExecDropExpression()

static ObjectAddress ATExecDropExpression ( Relation  rel,
const char colName,
bool  missing_ok,
LOCKMODE  lockmode 
)
static

Definition at line 8828 of file tablecmds.c.

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

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

8529{
8530 HeapTuple tuple;
8534 ObjectAddress address;
8535 Oid seqid;
8537 bool ispartitioned;
8538
8539 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8540 if (ispartitioned && !recurse)
8541 ereport(ERROR,
8543 errmsg("cannot drop identity from a column of only the partitioned table"),
8544 errhint("Do not specify the ONLY keyword.")));
8545
8546 if (rel->rd_rel->relispartition && !recursing)
8547 ereport(ERROR,
8549 errmsg("cannot drop identity from a column of a partition"));
8550
8553 if (!HeapTupleIsValid(tuple))
8554 ereport(ERROR,
8556 errmsg("column \"%s\" of relation \"%s\" does not exist",
8558
8560 attnum = attTup->attnum;
8561
8562 if (attnum <= 0)
8563 ereport(ERROR,
8565 errmsg("cannot alter system column \"%s\"",
8566 colName)));
8567
8568 if (!attTup->attidentity)
8569 {
8570 if (!missing_ok)
8571 ereport(ERROR,
8573 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8575 else
8576 {
8578 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8580 heap_freetuple(tuple);
8582 return InvalidObjectAddress;
8583 }
8584 }
8585
8586 attTup->attidentity = '\0';
8587 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8588
8590 RelationGetRelid(rel),
8591 attTup->attnum);
8593 RelationGetRelid(rel), attnum);
8594 heap_freetuple(tuple);
8595
8597
8598 /*
8599 * Recurse to drop the identity from column in partitions. Identity is
8600 * not inherited in regular inheritance children so ignore them.
8601 */
8602 if (recurse && ispartitioned)
8603 {
8604 List *children;
8605 ListCell *lc;
8606
8607 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8608
8609 foreach(lc, children)
8610 {
8612
8614 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8616 }
8617 }
8618
8619 if (!recursing)
8620 {
8621 /* drop the internal sequence */
8622 seqid = getIdentitySequence(rel, attnum, false);
8627 seqaddress.objectId = seqid;
8628 seqaddress.objectSubId = 0;
8630 }
8631
8632 return address;
8633}
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition dependency.c:274
#define PERFORM_DELETION_INTERNAL
Definition dependency.h:92
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:353
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition pg_depend.c:1018

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

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

◆ ATExecDropInherit()

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

Definition at line 17850 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecDropNotNull()

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

Definition at line 7781 of file tablecmds.c.

7783{
7784 HeapTuple tuple;
7789 ObjectAddress address;
7790
7791 /*
7792 * lookup the attribute
7793 */
7795
7797 if (!HeapTupleIsValid(tuple))
7798 ereport(ERROR,
7800 errmsg("column \"%s\" of relation \"%s\" does not exist",
7803 attnum = attTup->attnum;
7805 RelationGetRelid(rel), attnum);
7806
7807 /* If the column is already nullable there's nothing to do. */
7808 if (!attTup->attnotnull)
7809 {
7811 return InvalidObjectAddress;
7812 }
7813
7814 /* Prevent them from altering a system attribute */
7815 if (attnum <= 0)
7816 ereport(ERROR,
7818 errmsg("cannot alter system column \"%s\"",
7819 colName)));
7820
7821 if (attTup->attidentity)
7822 ereport(ERROR,
7824 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7826
7827 /*
7828 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7829 */
7830 if (rel->rd_rel->relispartition)
7831 {
7834 TupleDesc tupDesc = RelationGetDescr(parent);
7836
7838 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7839 ereport(ERROR,
7841 errmsg("column \"%s\" is marked NOT NULL in parent table",
7842 colName)));
7844 }
7845
7846 /*
7847 * Find the constraint that makes this column NOT NULL, and drop it.
7848 * dropconstraint_internal() resets attnotnull.
7849 */
7851 if (conTup == NULL)
7852 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7854
7855 /* The normal case: we have a pg_constraint row, remove it */
7856 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7857 false, lockmode);
7859
7861 RelationGetRelid(rel), attnum);
7862
7864
7865 return address;
7866}
bool attnotnull

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

Referenced by ATExecCmd().

◆ ATExecDropOf()

static void ATExecDropOf ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 18383 of file tablecmds.c.

18384{
18385 Oid relid = RelationGetRelid(rel);
18387 HeapTuple tuple;
18388
18389 if (!OidIsValid(rel->rd_rel->reloftype))
18390 ereport(ERROR,
18392 errmsg("\"%s\" is not a typed table",
18394
18395 /*
18396 * We don't bother to check ownership of the type --- ownership of the
18397 * table is presumed enough rights. No lock required on the type, either.
18398 */
18399
18400 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18402
18403 /* Clear pg_class.reloftype */
18406 if (!HeapTupleIsValid(tuple))
18407 elog(ERROR, "cache lookup failed for relation %u", relid);
18408 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18410
18412
18413 heap_freetuple(tuple);
18415}

References CatalogTupleUpdate(), DEPENDENCY_NORMAL, drop_parent_dependency(), elog, ereport, errcode(), errmsg(), ERROR, fb(), 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 17249 of file tablecmds.c.

17251{
17252 EnableDisableRule(rel, rulename, fires_when);
17253
17255 RelationGetRelid(rel), 0);
17256}
void EnableDisableRule(Relation rel, const char *rulename, char fires_when)

References EnableDisableRule(), fb(), 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 17231 of file tablecmds.c.

17234{
17235 EnableDisableTrigger(rel, trigname, InvalidOid,
17236 fires_when, skip_system, recurse,
17237 lockmode);
17238
17240 RelationGetRelid(rel), 0);
17241}
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition trigger.c:1726

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

Referenced by ATExecCmd().

◆ ATExecForceNoForceRowSecurity()

static void ATExecForceNoForceRowSecurity ( Relation  rel,
bool  force_rls 
)
static

Definition at line 18659 of file tablecmds.c.

18660{
18662 Oid relid;
18663 HeapTuple tuple;
18664
18665 relid = RelationGetRelid(rel);
18666
18668
18670
18671 if (!HeapTupleIsValid(tuple))
18672 elog(ERROR, "cache lookup failed for relation %u", relid);
18673
18675 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18676
18678 RelationGetRelid(rel), 0);
18679
18681 heap_freetuple(tuple);
18682}

References CatalogTupleUpdate(), elog, ERROR, fb(), 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 18688 of file tablecmds.c.

18689{
18691 ForeignServer *server;
18693 HeapTuple tuple;
18694 bool isnull;
18698 Datum datum;
18700
18701 if (options == NIL)
18702 return;
18703
18705
18707 ObjectIdGetDatum(rel->rd_id));
18708 if (!HeapTupleIsValid(tuple))
18709 ereport(ERROR,
18711 errmsg("foreign table \"%s\" does not exist",
18714 server = GetForeignServer(tableform->ftserver);
18715 fdw = GetForeignDataWrapper(server->fdwid);
18716
18717 memset(repl_val, 0, sizeof(repl_val));
18718 memset(repl_null, false, sizeof(repl_null));
18719 memset(repl_repl, false, sizeof(repl_repl));
18720
18721 /* Extract the current options */
18723 tuple,
18725 &isnull);
18726 if (isnull)
18727 datum = PointerGetDatum(NULL);
18728
18729 /* Transform the options */
18731 datum,
18732 options,
18733 fdw->fdwvalidator);
18734
18735 if (DatumGetPointer(datum) != NULL)
18737 else
18739
18741
18742 /* Everything looks good - update the tuple */
18743
18746
18747 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18748
18749 /*
18750 * Invalidate relcache so that all sessions will refresh any cached plans
18751 * that might depend on the old options.
18752 */
18754
18756 RelationGetRelid(rel), 0);
18757
18759
18760 heap_freetuple(tuple);
18761}

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

Referenced by ATExecCmd().

◆ ATExecMergePartitions()

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

Definition at line 22743 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecReplicaIdentity()

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

Definition at line 18515 of file tablecmds.c.

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

References IndexAmRoutine::amcanunique, elog, ereport, errcode(), errmsg(), ERROR, fb(), get_relname_relid(), index_close(), index_open(), IndexRelationGetNumberOfKeyAttributes, InvalidOid, 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 16552 of file tablecmds.c.

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

References Assert, CatalogTupleUpdate(), changeDependencyFor(), CommandCounterIncrement(), deleteDependencyRecordsForClass(), DEPENDENCY_NORMAL, elog, ERROR, fb(), 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 18769 of file tablecmds.c.

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

References attnum, CatalogTupleUpdate(), CommandCounterIncrement(), ereport, errcode(), errmsg(), ERROR, fb(), 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 8641 of file tablecmds.c.

8643{
8644 HeapTuple tuple;
8647 char attgenerated;
8648 bool rewrite;
8650 ObjectAddress address;
8651 Expr *defval;
8654
8656 if (!HeapTupleIsValid(tuple))
8657 ereport(ERROR,
8659 errmsg("column \"%s\" of relation \"%s\" does not exist",
8661
8663
8664 attnum = attTup->attnum;
8665 if (attnum <= 0)
8666 ereport(ERROR,
8668 errmsg("cannot alter system column \"%s\"",
8669 colName)));
8670
8671 attgenerated = attTup->attgenerated;
8672 if (!attgenerated)
8673 ereport(ERROR,
8675 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8677
8678 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8679 tab->verify_new_notnull = true;
8680
8681 /*
8682 * We need to prevent this because a change of expression could affect a
8683 * row filter and inject expressions that are not permitted in a row
8684 * filter. XXX We could try to have a more precise check to catch only
8685 * publications with row filters, or even re-verify the row filter
8686 * expressions.
8687 */
8688 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8690 ereport(ERROR,
8692 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8693 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8695
8696 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8697
8698 ReleaseSysCache(tuple);
8699
8700 if (rewrite)
8701 {
8702 /*
8703 * Clear all the missing values if we're rewriting the table, since
8704 * this renders them pointless.
8705 */
8707
8708 /* make sure we don't conflict with later attribute modifications */
8710 }
8711
8712 /*
8713 * Find everything that depends on the column (constraints, indexes, etc),
8714 * and record enough information to let us recreate the objects.
8715 */
8717
8718 /*
8719 * Drop the dependency records of the GENERATED expression, in particular
8720 * its INTERNAL dependency on the column, which would otherwise cause
8721 * dependency.c to refuse to perform the deletion.
8722 */
8724 if (!OidIsValid(attrdefoid))
8725 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8726 RelationGetRelid(rel), attnum);
8728
8729 /* Make above changes visible */
8731
8732 /*
8733 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8734 * safety, but at present we do not expect anything to depend on the
8735 * expression.
8736 */
8738 false, false);
8739
8740 /* Prepare to store the new expression, in the catalogs */
8742 rawEnt->attnum = attnum;
8743 rawEnt->raw_default = newExpr;
8744 rawEnt->generated = attgenerated;
8745
8746 /* Store the generated expression */
8748 false, true, false, NULL);
8749
8750 /* Make above new expression visible */
8752
8753 if (rewrite)
8754 {
8755 /* Prepare for table rewrite */
8756 defval = (Expr *) build_column_default(rel, attnum);
8757
8759 newval->attnum = attnum;
8760 newval->expr = expression_planner(defval);
8761 newval->is_generated = true;
8762
8763 tab->newvals = lappend(tab->newvals, newval);
8765 }
8766
8767 /* Drop any pg_statistic entry for the column */
8769
8771 RelationGetRelid(rel), attnum);
8772
8774 RelationGetRelid(rel), attnum);
8775 return address;
8776}
List * GetRelationPublications(Oid relid)

References AddRelationNewConstraints(), AT_REWRITE_DEFAULT_VAL, AT_SetExpression, attnum, build_column_default(), CommandCounterIncrement(), deleteDependencyRecordsFor(), DROP_RESTRICT, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, expression_planner(), fb(), GetAttrDefaultOid(), GetRelationPublications(), GETSTRUCT(), HeapTupleIsValid, InvokeObjectPostAlterHook, lappend(), list_make1, newval, AlteredTableInfo::newvals, NIL, ObjectAddressSubSet, OidIsValid, palloc0_object, palloc_object, RelationClearMissing(), RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RememberAllDependentForRebuilding(), RemoveAttrDefault(), RemoveStatistics(), AlteredTableInfo::rewrite, SearchSysCacheAttName(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATExecCmd().

◆ ATExecSetIdentity()

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

Definition at line 8410 of file tablecmds.c.

8412{
8415 HeapTuple tuple;
8419 ObjectAddress address;
8420 bool ispartitioned;
8421
8422 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8423 if (ispartitioned && !recurse)
8424 ereport(ERROR,
8426 errmsg("cannot change identity column of only the partitioned table"),
8427 errhint("Do not specify the ONLY keyword.")));
8428
8429 if (rel->rd_rel->relispartition && !recursing)
8430 ereport(ERROR,
8432 errmsg("cannot change identity column of a partition"));
8433
8434 foreach(option, castNode(List, def))
8435 {
8437
8438 if (strcmp(defel->defname, "generated") == 0)
8439 {
8440 if (generatedEl)
8441 ereport(ERROR,
8443 errmsg("conflicting or redundant options")));
8445 }
8446 else
8447 elog(ERROR, "option \"%s\" not recognized",
8448 defel->defname);
8449 }
8450
8451 /*
8452 * Even if there is nothing to change here, we run all the checks. There
8453 * will be a subsequent ALTER SEQUENCE that relies on everything being
8454 * there.
8455 */
8456
8459 if (!HeapTupleIsValid(tuple))
8460 ereport(ERROR,
8462 errmsg("column \"%s\" of relation \"%s\" does not exist",
8464
8466 attnum = attTup->attnum;
8467
8468 if (attnum <= 0)
8469 ereport(ERROR,
8471 errmsg("cannot alter system column \"%s\"",
8472 colName)));
8473
8474 if (!attTup->attidentity)
8475 ereport(ERROR,
8477 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8479
8480 if (generatedEl)
8481 {
8482 attTup->attidentity = defGetInt32(generatedEl);
8483 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8484
8486 RelationGetRelid(rel),
8487 attTup->attnum);
8489 RelationGetRelid(rel), attnum);
8490 }
8491 else
8492 address = InvalidObjectAddress;
8493
8494 heap_freetuple(tuple);
8496
8497 /*
8498 * Recurse to propagate the identity change to partitions. Identity is not
8499 * inherited in regular inheritance children.
8500 */
8501 if (generatedEl && recurse && ispartitioned)
8502 {
8503 List *children;
8504 ListCell *lc;
8505
8506 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8507
8508 foreach(lc, children)
8509 {
8511
8513 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8515 }
8516 }
8517
8518 return address;
8519}
int32 defGetInt32(DefElem *def)
Definition define.c:148
#define lfirst_node(type, lc)
Definition pg_list.h:176

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

Referenced by ATExecCmd(), and ATExecSetIdentity().

◆ ATExecSetNotNull()

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

Definition at line 7952 of file tablecmds.c.

7954{
7955 HeapTuple tuple;
7957 ObjectAddress address;
7958 Constraint *constraint;
7960 List *cooked;
7961 bool is_no_inherit = false;
7962
7963 /* Guard against stack overflow due to overly deep inheritance tree. */
7965
7966 /* At top level, permission check was done in ATPrepCmd, else do it */
7967 if (recursing)
7968 {
7971 Assert(conName != NULL);
7972 }
7973
7976 ereport(ERROR,
7978 errmsg("column \"%s\" of relation \"%s\" does not exist",
7980
7981 /* Prevent them from altering a system attribute */
7982 if (attnum <= 0)
7983 ereport(ERROR,
7985 errmsg("cannot alter system column \"%s\"",
7986 colName)));
7987
7988 /* See if there's already a constraint */
7990 if (HeapTupleIsValid(tuple))
7991 {
7993 bool changed = false;
7994
7995 /*
7996 * Don't let a NO INHERIT constraint be changed into inherit.
7997 */
7998 if (conForm->connoinherit && recurse)
7999 ereport(ERROR,
8001 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
8002 NameStr(conForm->conname),
8004
8005 /*
8006 * If we find an appropriate constraint, we're almost done, but just
8007 * need to change some properties on it: if we're recursing, increment
8008 * coninhcount; if not, set conislocal if not already set.
8009 */
8010 if (recursing)
8011 {
8012 if (pg_add_s16_overflow(conForm->coninhcount, 1,
8013 &conForm->coninhcount))
8014 ereport(ERROR,
8016 errmsg("too many inheritance parents"));
8017 changed = true;
8018 }
8019 else if (!conForm->conislocal)
8020 {
8021 conForm->conislocal = true;
8022 changed = true;
8023 }
8024 else if (!conForm->convalidated)
8025 {
8026 /*
8027 * Flip attnotnull and convalidated, and also validate the
8028 * constraint.
8029 */
8030 return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
8031 recurse, recursing, lockmode);
8032 }
8033
8034 if (changed)
8035 {
8037
8039
8040 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
8043 }
8044
8045 if (changed)
8046 return address;
8047 else
8048 return InvalidObjectAddress;
8049 }
8050
8051 /*
8052 * If we're asked not to recurse, and children exist, raise an error for
8053 * partitioned tables. For inheritance, we act as if NO INHERIT had been
8054 * specified.
8055 */
8056 if (!recurse &&
8058 NoLock) != NIL)
8059 {
8060 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8061 ereport(ERROR,
8063 errmsg("constraint must be added to child tables too"),
8064 errhint("Do not specify the ONLY keyword."));
8065 else
8066 is_no_inherit = true;
8067 }
8068
8069 /*
8070 * No constraint exists; we must add one. First determine a name to use,
8071 * if we haven't already.
8072 */
8073 if (!recursing)
8074 {
8075 Assert(conName == NULL);
8077 colName, "not_null",
8079 NIL);
8080 }
8081
8083 constraint->is_no_inherit = is_no_inherit;
8084 constraint->conname = conName;
8085
8086 /* and do it */
8087 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8088 false, !recursing, false, NULL);
8089 ccon = linitial(cooked);
8090 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8091
8093 RelationGetRelid(rel), attnum);
8094
8095 /* Mark pg_attribute.attnotnull for the column and queue validation */
8096 set_attnotnull(wqueue, rel, attnum, true, true);
8097
8098 /*
8099 * Recurse to propagate the constraint to children that don't have one.
8100 */
8101 if (recurse)
8102 {
8103 List *children;
8104
8106 lockmode);
8107
8108 foreach_oid(childoid, children)
8109 {
8111
8113
8115 recurse, true, lockmode);
8117 }
8118 }
8119
8120 return address;
8121}
Constraint * makeNotNullConstraint(String *colname)
Definition makefuncs.c:493
String * makeString(char *str)
Definition value.c:63

References AddRelationNewConstraints(), Assert, AT_AddConstraint, ATExecSetNotNull(), ATExecValidateConstraint(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, attnum, CatalogTupleUpdate(), check_stack_depth(), ChooseConstraintName(), CommandCounterIncrement(), Constraint::conname, ereport, errcode(), errhint(), errmsg(), ERROR, fb(), find_inheritance_children(), findNotNullConstraintAttnum(), foreach_oid, Form_pg_constraint, get_attnum(), GETSTRUCT(), HeapTupleIsValid, InvalidAttrNumber, InvalidObjectAddress, InvokeObjectPostAlterHook, Constraint::is_no_inherit, linitial, list_make1, makeNotNullConstraint(), makeString(), NameStr, NIL, NoLock, ObjectAddressSet, pg_add_s16_overflow(), RelationData::rd_rel, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, set_attnotnull(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecAlterConstrInheritability(), ATExecCmd(), and ATExecSetNotNull().

◆ ATExecSetOptions()

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

Definition at line 9077 of file tablecmds.c.

9079{
9081 HeapTuple tuple,
9082 newtuple;
9085 Datum datum,
9086 newOptions;
9087 bool isnull;
9088 ObjectAddress address;
9092
9094
9096
9097 if (!HeapTupleIsValid(tuple))
9098 ereport(ERROR,
9100 errmsg("column \"%s\" of relation \"%s\" does not exist",
9103
9104 attnum = attrtuple->attnum;
9105 if (attnum <= 0)
9106 ereport(ERROR,
9108 errmsg("cannot alter system column \"%s\"",
9109 colName)));
9110
9111 /* Generate new proposed attoptions (text array) */
9113 &isnull);
9114 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9116 false, isReset);
9117 /* Validate new options */
9119
9120 /* Build new tuple. */
9121 memset(repl_null, false, sizeof(repl_null));
9122 memset(repl_repl, false, sizeof(repl_repl));
9123 if (newOptions != (Datum) 0)
9125 else
9130
9131 /* Update system catalog. */
9132 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9133
9135 RelationGetRelid(rel),
9136 attrtuple->attnum);
9138 RelationGetRelid(rel), attnum);
9139
9140 heap_freetuple(newtuple);
9141
9142 ReleaseSysCache(tuple);
9143
9145
9146 return address;
9147}
Datum transformRelOptions(Datum oldOptions, List *defList, const char *nameSpace, const char *const validnsps[], bool acceptOidsOff, bool isReset)
bytea * attribute_reloptions(Datum reloptions, bool validate)

References attnum, attribute_reloptions(), castNode, CatalogTupleUpdate(), ereport, errcode(), errmsg(), ERROR, fb(), 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 16672 of file tablecmds.c.

16674{
16675 Oid relid;
16677 HeapTuple tuple;
16678 HeapTuple newtuple;
16679 Datum datum;
16684 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16685
16686 if (defList == NIL && operation != AT_ReplaceRelOptions)
16687 return; /* nothing to do */
16688
16690
16691 /* Fetch heap tuple */
16692 relid = RelationGetRelid(rel);
16694 if (!HeapTupleIsValid(tuple))
16695 elog(ERROR, "cache lookup failed for relation %u", relid);
16696
16697 if (operation == AT_ReplaceRelOptions)
16698 {
16699 /*
16700 * If we're supposed to replace the reloptions list, we just pretend
16701 * there were none before.
16702 */
16703 datum = (Datum) 0;
16704 }
16705 else
16706 {
16707 bool isnull;
16708
16709 /* Get the old reloptions */
16711 &isnull);
16712 if (isnull)
16713 datum = (Datum) 0;
16714 }
16715
16716 /* Generate new proposed reloptions (text array) */
16718 operation == AT_ResetRelOptions);
16719
16720 /* Validate */
16721 switch (rel->rd_rel->relkind)
16722 {
16723 case RELKIND_RELATION:
16724 case RELKIND_MATVIEW:
16725 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16726 break;
16729 break;
16730 case RELKIND_VIEW:
16732 break;
16733 case RELKIND_INDEX:
16736 break;
16737 case RELKIND_TOASTVALUE:
16738 /* fall through to error -- shouldn't ever get here */
16739 default:
16740 ereport(ERROR,
16742 errmsg("cannot set options for relation \"%s\"",
16744 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16745 break;
16746 }
16747
16748 /* Special-case validation of view options */
16749 if (rel->rd_rel->relkind == RELKIND_VIEW)
16750 {
16753 ListCell *cell;
16754 bool check_option = false;
16755
16756 foreach(cell, view_options)
16757 {
16758 DefElem *defel = (DefElem *) lfirst(cell);
16759
16760 if (strcmp(defel->defname, "check_option") == 0)
16761 check_option = true;
16762 }
16763
16764 /*
16765 * If the check option is specified, look to see if the view is
16766 * actually auto-updatable or not.
16767 */
16768 if (check_option)
16769 {
16770 const char *view_updatable_error =
16772
16774 ereport(ERROR,
16776 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16777 errhint("%s", _(view_updatable_error))));
16778 }
16779 }
16780
16781 /*
16782 * All we need do here is update the pg_class row; the new options will be
16783 * propagated into relcaches during post-commit cache inval.
16784 */
16785 memset(repl_val, 0, sizeof(repl_val));
16786 memset(repl_null, false, sizeof(repl_null));
16787 memset(repl_repl, false, sizeof(repl_repl));
16788
16789 if (newOptions != (Datum) 0)
16791 else
16793
16795
16796 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16798
16799 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16801
16803
16804 heap_freetuple(newtuple);
16805
16806 ReleaseSysCache(tuple);
16807
16808 /* repeat the whole exercise for the toast table, if there's one */
16809 if (OidIsValid(rel->rd_rel->reltoastrelid))
16810 {
16812 Oid toastid = rel->rd_rel->reltoastrelid;
16813
16814 toastrel = table_open(toastid, lockmode);
16815
16816 /* Fetch heap tuple */
16818 if (!HeapTupleIsValid(tuple))
16819 elog(ERROR, "cache lookup failed for relation %u", toastid);
16820
16821 if (operation == AT_ReplaceRelOptions)
16822 {
16823 /*
16824 * If we're supposed to replace the reloptions list, we just
16825 * pretend there were none before.
16826 */
16827 datum = (Datum) 0;
16828 }
16829 else
16830 {
16831 bool isnull;
16832
16833 /* Get the old reloptions */
16835 &isnull);
16836 if (isnull)
16837 datum = (Datum) 0;
16838 }
16839
16841 false, operation == AT_ResetRelOptions);
16842
16844
16845 memset(repl_val, 0, sizeof(repl_val));
16846 memset(repl_null, false, sizeof(repl_null));
16847 memset(repl_repl, false, sizeof(repl_repl));
16848
16849 if (newOptions != (Datum) 0)
16851 else
16853
16855
16856 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
16858
16859 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
16860
16863 InvalidOid, true);
16864
16865 heap_freetuple(newtuple);
16866
16867 ReleaseSysCache(tuple);
16868
16870 }
16871
16873}
#define _(x)
Definition elog.c:95
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
List * untransformRelOptions(Datum options)
bytea * view_reloptions(Datum reloptions, bool validate)
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
#define HEAP_RELOPT_NAMESPACES
Definition reloptions.h:62
Query * get_view_query(Relation view)
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
amoptions_function amoptions
Definition amapi.h:304
HeapTuple SearchSysCacheLocked1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:282

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

Referenced by ATExecCmd().

◆ ATExecSetRowSecurity()

static void ATExecSetRowSecurity ( Relation  rel,
bool  rls 
)
static

Definition at line 18629 of file tablecmds.c.

18630{
18632 Oid relid;
18633 HeapTuple tuple;
18634
18635 relid = RelationGetRelid(rel);
18636
18637 /* Pull the record for this relation and update it */
18639
18641
18642 if (!HeapTupleIsValid(tuple))
18643 elog(ERROR, "cache lookup failed for relation %u", relid);
18644
18646 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18647
18649 RelationGetRelid(rel), 0);
18650
18652 heap_freetuple(tuple);
18653}

References CatalogTupleUpdate(), elog, ERROR, fb(), 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 8932 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecSetStorage()

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

Definition at line 9219 of file tablecmds.c.

9220{
9222 HeapTuple tuple;
9225 ObjectAddress address;
9226
9228
9230
9231 if (!HeapTupleIsValid(tuple))
9232 ereport(ERROR,
9234 errmsg("column \"%s\" of relation \"%s\" does not exist",
9237
9238 attnum = attrtuple->attnum;
9239 if (attnum <= 0)
9240 ereport(ERROR,
9242 errmsg("cannot alter system column \"%s\"",
9243 colName)));
9244
9245 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9246
9247 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9248
9250 RelationGetRelid(rel),
9251 attrtuple->attnum);
9252
9253 /*
9254 * Apply the change to indexes as well (only for simple index columns,
9255 * matching behavior of index.c ConstructTupleDescriptor()).
9256 */
9258 true, attrtuple->attstorage,
9259 false, 0,
9260 lockmode);
9261
9262 heap_freetuple(tuple);
9263
9265
9267 RelationGetRelid(rel), attnum);
9268 return address;
9269}
static char GetAttributeStorage(Oid atttypid, const char *storagemode)

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

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

References Assert, ATExecSetTableSpace(), CheckRelationTableSpaceMove(), CommandCounterIncrement(), fb(), 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(), and table_relation_copy_data().

Referenced by ATExecSetTableSpace(), and ATRewriteTables().

◆ ATExecSetTableSpaceNoStorage()

static void ATExecSetTableSpaceNoStorage ( Relation  rel,
Oid  newTableSpace 
)
static

Definition at line 16973 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecSplitPartition()

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

Definition at line 23197 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecValidateConstraint()

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

Definition at line 12935 of file tablecmds.c.

12937{
12939 SysScanDesc scan;
12940 ScanKeyData skey[3];
12941 HeapTuple tuple;
12943 ObjectAddress address;
12944
12946
12947 /*
12948 * Find and check the target constraint
12949 */
12950 ScanKeyInit(&skey[0],
12954 ScanKeyInit(&skey[1],
12958 ScanKeyInit(&skey[2],
12963 true, NULL, 3, skey);
12964
12965 /* There can be at most one matching row */
12966 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
12967 ereport(ERROR,
12969 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12971
12972 con = (Form_pg_constraint) GETSTRUCT(tuple);
12973 if (con->contype != CONSTRAINT_FOREIGN &&
12974 con->contype != CONSTRAINT_CHECK &&
12975 con->contype != CONSTRAINT_NOTNULL)
12976 ereport(ERROR,
12978 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
12980 errdetail("This operation is not supported for this type of constraint."));
12981
12982 if (!con->conenforced)
12983 ereport(ERROR,
12985 errmsg("cannot validate NOT ENFORCED constraint")));
12986
12987 if (!con->convalidated)
12988 {
12989 if (con->contype == CONSTRAINT_FOREIGN)
12990 {
12991 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
12992 tuple, lockmode);
12993 }
12994 else if (con->contype == CONSTRAINT_CHECK)
12995 {
12997 tuple, recurse, recursing, lockmode);
12998 }
12999 else if (con->contype == CONSTRAINT_NOTNULL)
13000 {
13002 tuple, recurse, recursing, lockmode);
13003 }
13004
13005 ObjectAddressSet(address, ConstraintRelationId, con->oid);
13006 }
13007 else
13008 address = InvalidObjectAddress; /* already validated */
13009
13010 systable_endscan(scan);
13011
13013
13014 return address;
13015}
static void QueueFKConstraintValidation(List **wqueue, Relation conrel, Relation fkrel, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode)
static void QueueNNConstraintValidation(List **wqueue, Relation conrel, Relation rel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
static void QueueCheckConstraintValidation(List **wqueue, Relation conrel, Relation rel, char *constrName, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)

References BTEqualStrategyNumber, CStringGetDatum(), ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), Form_pg_constraint, GETSTRUCT(), HeapTupleIsValid, InvalidObjectAddress, InvalidOid, ObjectAddressSet, ObjectIdGetDatum(), QueueCheckConstraintValidation(), QueueFKConstraintValidation(), QueueNNConstraintValidation(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecCmd(), ATExecSetNotNull(), QueueCheckConstraintValidation(), and QueueNNConstraintValidation().

◆ ATGetQueueEntry()

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

Definition at line 6597 of file tablecmds.c.

6598{
6599 Oid relid = RelationGetRelid(rel);
6600 AlteredTableInfo *tab;
6601 ListCell *ltab;
6602
6603 foreach(ltab, *wqueue)
6604 {
6605 tab = (AlteredTableInfo *) lfirst(ltab);
6606 if (tab->relid == relid)
6607 return tab;
6608 }
6609
6610 /*
6611 * Not there, so add it. Note that we make a copy of the relation's
6612 * existing descriptor before anything interesting can happen to it.
6613 */
6615 tab->relid = relid;
6616 tab->rel = NULL; /* set later */
6617 tab->relkind = rel->rd_rel->relkind;
6620 tab->chgAccessMethod = false;
6623 tab->chgPersistence = false;
6624
6625 *wqueue = lappend(*wqueue, tab);
6626
6627 return tab;
6628}
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition tupdesc.c:323

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

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

◆ ATParseTransformCmd()

static AlterTableCmd * ATParseTransformCmd ( List **  wqueue,
AlteredTableInfo tab,
Relation  rel,
AlterTableCmd cmd,
bool  recurse,
LOCKMODE  lockmode,
AlterTablePass  cur_pass,
AlterTableUtilityContext context 
)
static

Definition at line 5746 of file tablecmds.c.

5749{
5753 List *afterStmts;
5754 ListCell *lc;
5755
5756 /* Gin up an AlterTableStmt with just this subcommand and this table */
5757 atstmt->relation =
5760 -1);
5761 atstmt->relation->inh = recurse;
5762 atstmt->cmds = list_make1(cmd);
5763 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5764 atstmt->missing_ok = false;
5765
5766 /* Transform the AlterTableStmt */
5768 atstmt,
5769 context->queryString,
5770 &beforeStmts,
5771 &afterStmts);
5772
5773 /* Execute any statements that should happen before these subcommand(s) */
5774 foreach(lc, beforeStmts)
5775 {
5776 Node *stmt = (Node *) lfirst(lc);
5777
5780 }
5781
5782 /* Examine the transformed subcommands and schedule them appropriately */
5783 foreach(lc, atstmt->cmds)
5784 {
5787
5788 /*
5789 * This switch need only cover the subcommand types that can be added
5790 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5791 * executing the subcommand immediately, as a substitute for the
5792 * original subcommand. (Note, however, that this does cause
5793 * AT_AddConstraint subcommands to be rescheduled into later passes,
5794 * which is important for index and foreign key constraints.)
5795 *
5796 * We assume we needn't do any phase-1 checks for added subcommands.
5797 */
5798 switch (cmd2->subtype)
5799 {
5800 case AT_AddIndex:
5802 break;
5805 break;
5806 case AT_AddConstraint:
5807 /* Recursion occurs during execution phase */
5808 if (recurse)
5809 cmd2->recurse = true;
5810 switch (castNode(Constraint, cmd2->def)->contype)
5811 {
5812 case CONSTR_NOTNULL:
5814 break;
5815 case CONSTR_PRIMARY:
5816 case CONSTR_UNIQUE:
5817 case CONSTR_EXCLUSION:
5819 break;
5820 default:
5822 break;
5823 }
5824 break;
5826 /* This command never recurses */
5827 /* No command-specific prep needed */
5829 break;
5830 default:
5831 pass = cur_pass;
5832 break;
5833 }
5834
5835 if (pass < cur_pass)
5836 {
5837 /* Cannot schedule into a pass we already finished */
5838 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5839 pass);
5840 }
5841 else if (pass > cur_pass)
5842 {
5843 /* OK, queue it up for later */
5844 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5845 }
5846 else
5847 {
5848 /*
5849 * We should see at most one subcommand for the current pass,
5850 * which is the transformed version of the original subcommand.
5851 */
5852 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5853 {
5854 /* Found the transformed version of our subcommand */
5855 newcmd = cmd2;
5856 }
5857 else
5858 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5859 pass);
5860 }
5861 }
5862
5863 /* Queue up any after-statements to happen at the end */
5864 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5865
5866 return newcmd;
5867}
List * list_concat(List *list1, const List *list2)
Definition list.c:561
AlterTableStmt * transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, const char *queryString, List **beforeStmts, List **afterStmts)
List * subcmds[AT_NUM_PASSES]
Definition tablecmds.c:186
void ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context)
Definition utility.c:1959

References AlteredTableInfo::afterStmts, AT_AddConstraint, AT_AddIndex, AT_AddIndexConstraint, AT_AlterColumnGenericOptions, AT_PASS_ADD_INDEX, AT_PASS_ADD_INDEXCONSTR, AT_PASS_ADD_OTHERCONSTR, AT_PASS_COL_ATTRS, AT_PASS_MISC, castNode, CommandCounterIncrement(), CONSTR_EXCLUSION, CONSTR_NOTNULL, CONSTR_PRIMARY, CONSTR_UNIQUE, elog, ERROR, fb(), get_namespace_name(), lappend(), lfirst, lfirst_node, list_concat(), list_make1, makeNode, makeRangeVar(), OBJECT_TABLE, ProcessUtilityForAlterTable(), pstrdup(), AlterTableUtilityContext::queryString, 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 15463 of file tablecmds.c.

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

References AccessExclusiveLock, add_exact_object_address(), AT_ClusterOn, AT_PASS_OLD_CONSTR, AT_ReplicaIdentity, ATPostAlterTypeParse(), AlteredTableInfo::changedConstraintDefs, AlteredTableInfo::changedConstraintOids, AlteredTableInfo::changedIndexDefs, AlteredTableInfo::changedIndexOids, AlteredTableInfo::changedStatisticsDefs, AlteredTableInfo::changedStatisticsOids, AlteredTableInfo::clusterOnIndex, AlterTableCmd::def, DROP_RESTRICT, elog, ERROR, fb(), forboth, Form_pg_constraint, free_object_addresses(), get_typ_typrelid(), getBaseType(), GETSTRUCT(), HeapTupleIsValid, IndexGetRelation(), InvalidOid, lappend(), lfirst, lfirst_oid, LockRelationOid(), makeNode, AlterTableCmd::name, new_object_addresses(), ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, PERFORM_DELETION_INTERNAL, performMultipleDeletions(), ReleaseSysCache(), AlteredTableInfo::relid, AlteredTableInfo::replicaIdentityIndex, AlteredTableInfo::rewrite, SearchSysCache1(), ShareUpdateExclusiveLock, StatisticsGetRelation(), AlteredTableInfo::subcmds, and AlterTableCmd::subtype.

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

15657{
15661 Relation rel;
15662
15663 /*
15664 * We expect that we will get only ALTER TABLE and CREATE INDEX
15665 * statements. Hence, there is no need to pass them through
15666 * parse_analyze_*() or the rewriter, but instead we need to pass them
15667 * through parse_utilcmd.c to make them ready for execution.
15668 */
15672 {
15674 Node *stmt = rs->stmt;
15675
15676 if (IsA(stmt, IndexStmt))
15679 (IndexStmt *) stmt,
15680 cmd));
15681 else if (IsA(stmt, AlterTableStmt))
15682 {
15684 List *afterStmts;
15685
15687 (AlterTableStmt *) stmt,
15688 cmd,
15689 &beforeStmts,
15690 &afterStmts);
15694 }
15695 else if (IsA(stmt, CreateStatsStmt))
15699 cmd));
15700 else
15702 }
15703
15704 /* Caller should already have acquired whatever lock we need. */
15706
15707 /*
15708 * Attach each generated command to the proper place in the work queue.
15709 * Note this could result in creation of entirely new work-queue entries.
15710 *
15711 * Also note that we have to tweak the command subtypes, because it turns
15712 * out that re-creation of indexes and constraints has to act a bit
15713 * differently from initial creation.
15714 */
15715 foreach(list_item, querytree_list)
15716 {
15717 Node *stm = (Node *) lfirst(list_item);
15718 AlteredTableInfo *tab;
15719
15720 tab = ATGetQueueEntry(wqueue, rel);
15721
15722 if (IsA(stm, IndexStmt))
15723 {
15724 IndexStmt *stmt = (IndexStmt *) stm;
15726
15727 if (!rewrite)
15729 stmt->reset_default_tblspc = true;
15730 /* keep the index's comment */
15731 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15732
15734 newcmd->subtype = AT_ReAddIndex;
15735 newcmd->def = (Node *) stmt;
15738 }
15739 else if (IsA(stm, AlterTableStmt))
15740 {
15742 ListCell *lcmd;
15743
15744 foreach(lcmd, stmt->cmds)
15745 {
15747
15748 if (cmd->subtype == AT_AddIndex)
15749 {
15751 Oid indoid;
15752
15753 indstmt = castNode(IndexStmt, cmd->def);
15754 indoid = get_constraint_index(oldId);
15755
15756 if (!rewrite)
15757 TryReuseIndex(indoid, indstmt);
15758 /* keep any comment on the index */
15759 indstmt->idxcomment = GetComment(indoid,
15761 indstmt->reset_default_tblspc = true;
15762
15763 cmd->subtype = AT_ReAddIndex;
15765 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15766
15767 /* recreate any comment on the constraint */
15770 oldId,
15771 rel,
15772 NIL,
15773 indstmt->idxname);
15774 }
15775 else if (cmd->subtype == AT_AddConstraint)
15776 {
15777 Constraint *con = castNode(Constraint, cmd->def);
15778
15780 /* rewriting neither side of a FK */
15781 if (con->contype == CONSTR_FOREIGN &&
15782 !rewrite && tab->rewrite == 0)
15784 con->reset_default_tblspc = true;
15788
15789 /*
15790 * Recreate any comment on the constraint. If we have
15791 * recreated a primary key, then transformTableConstraint
15792 * has added an unnamed not-null constraint here; skip
15793 * this in that case.
15794 */
15795 if (con->conname)
15798 oldId,
15799 rel,
15800 NIL,
15801 con->conname);
15802 else
15803 Assert(con->contype == CONSTR_NOTNULL);
15804 }
15805 else
15806 elog(ERROR, "unexpected statement subtype: %d",
15807 (int) cmd->subtype);
15808 }
15809 }
15810 else if (IsA(stm, AlterDomainStmt))
15811 {
15813
15814 if (stmt->subtype == AD_AddConstraint)
15815 {
15816 Constraint *con = castNode(Constraint, stmt->def);
15818
15820 cmd->def = (Node *) stmt;
15823
15824 /* recreate any comment on the constraint */
15827 oldId,
15828 NULL,
15829 stmt->typeName,
15830 con->conname);
15831 }
15832 else
15833 elog(ERROR, "unexpected statement subtype: %d",
15834 (int) stmt->subtype);
15835 }
15836 else if (IsA(stm, CreateStatsStmt))
15837 {
15840
15841 /* keep the statistics object's comment */
15842 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
15843
15845 newcmd->subtype = AT_ReAddStatistics;
15846 newcmd->def = (Node *) stmt;
15847 tab->subcmds[AT_PASS_MISC] =
15849 }
15850 else
15851 elog(ERROR, "unexpected statement type: %d",
15852 (int) nodeTag(stm));
15853 }
15854
15855 relation_close(rel, NoLock);
15856}
List * raw_parser(const char *str, RawParseMode mode)
Definition parser.c:42
char * GetComment(Oid oid, Oid classoid, int32 subid)
Definition comment.c:420
Oid get_constraint_index(Oid conoid)
Definition lsyscache.c:1189
#define nodeTag(nodeptr)
Definition nodes.h:139
IndexStmt * transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString)
CreateStatsStmt * transformStatsStmt(Oid relid, CreateStatsStmt *stmt, const char *queryString)
@ AD_AddConstraint
@ RAW_PARSE_DEFAULT
Definition parser.h:39
bool reset_default_tblspc
Oid old_pktable_oid
Node * stmt
static void TryReuseIndex(Oid oldId, IndexStmt *stmt)
static void TryReuseForeignKey(Oid oldId, Constraint *con)
static void RebuildConstraintComment(AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)

References AD_AddConstraint, Assert, AT_AddConstraint, AT_AddIndex, AT_PASS_MISC, AT_PASS_OLD_CONSTR, AT_PASS_OLD_INDEX, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReAddIndex, AT_ReAddStatistics, ATGetQueueEntry(), castNode, Constraint::conname, CONSTR_FOREIGN, CONSTR_NOTNULL, Constraint::contype, AlterTableCmd::def, elog, ERROR, fb(), get_constraint_index(), GetComment(), 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, 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 7232 of file tablecmds.c.

7235{
7236 if (rel->rd_rel->reloftype && !recursing)
7237 ereport(ERROR,
7239 errmsg("cannot add column to typed table")));
7240
7241 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7242 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7243
7244 if (recurse && !is_view)
7245 cmd->recurse = true;
7246}
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:6930

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

Referenced by ATPrepCmd().

◆ ATPrepAddInherit()

static void ATPrepAddInherit ( Relation  child_rel)
static

Definition at line 17266 of file tablecmds.c.

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

References ereport, errcode(), errmsg(), ERROR, and fb().

Referenced by ATPrepCmd().

◆ ATPrepAddPrimaryKey()

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

Definition at line 9525 of file tablecmds.c.

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

References AT_AddConstraint, ATPrepCmd(), castNode, CONSTR_PRIMARY, AlterTableCmd::def, ereport, errmsg(), ERROR, fb(), find_inheritance_children(), findNotNullConstraint(), foreach_node, foreach_oid, get_rel_name(), heap_freetuple(), makeNode, makeNotNullConstraint(), NIL, RelationGetRelid, strVal, and verifyNotNullPKCompatible().

Referenced by ATPrepCmd().

◆ ATPrepAlterColumnType()

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

Definition at line 14400 of file tablecmds.c.

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

References ACL_USAGE, aclcheck_error_type(), ACLCHECK_OK, assign_expr_collations(), AT_REWRITE_COLUMN_REWRITE, ATColumnChangeRequiresRewrite(), ATPrepCmd(), attnum, ATTypedTableRecursion(), bms_make_singleton(), build_attrmap_by_name(), CheckAlterTableIsSafe(), CheckAttributeType(), CHKATYPE_IS_VIRTUAL, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, ColumnDef::cooked_default, copyObject, AlterTableCmd::def, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, expand_generated_columns_in_expr(), expression_planner(), exprType(), fb(), find_all_inheritors(), find_composite_type_dependencies(), find_inheritance_children(), FirstLowInvalidHeapAttributeNumber, forboth, format_type_be(), format_type_with_typemod(), GetColumnDefCollation(), GETSTRUCT(), GetUserId(), has_partition_attrs(), HeapTupleIsValid, InvalidOid, lappend(), lfirst_int, lfirst_oid, list_make1_oid, ColumnDef::location, make_parsestate(), makeVar(), map_variable_attnos(), AlterTableCmd::name, newval, AlteredTableInfo::newvals, NIL, NoLock, object_aclcheck(), ParseState::p_sourcetext, palloc0_object, parser_errposition(), pfree(), AlterTableUtilityContext::queryString, quote_identifier(), RelationData::rd_rel, relation_close(), relation_open(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), AlteredTableInfo::relkind, AlteredTableInfo::rewrite, SearchSysCacheAttName(), ColumnDef::typeName, and typenameTypeIdAndMod().

Referenced by ATPrepCmd().

◆ ATPrepChangePersistence()

static void ATPrepChangePersistence ( AlteredTableInfo tab,
Relation  rel,
bool  toLogged 
)
static

Definition at line 18846 of file tablecmds.c.

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

References AccessShareLock, AT_REWRITE_ALTER_PERSISTENCE, BTEqualStrategyNumber, AlteredTableInfo::chgPersistence, ereport, errcode(), errdetail(), errmsg(), ERROR, errtable(), errtableconstraint(), fb(), Form_pg_constraint, GetRelationPublications(), GETSTRUCT(), HeapTupleIsValid, InvalidOid, NameStr, AlteredTableInfo::newrelpersistence, NIL, ObjectIdGetDatum(), RelationData::rd_rel, relation_close(), relation_open(), RelationGetRelationName, RelationGetRelid, RelationIsPermanent, AlteredTableInfo::rewrite, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATPrepCmd().

◆ ATPrepCmd()

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

Definition at line 4918 of file tablecmds.c.

4921{
4922 AlteredTableInfo *tab;
4924
4925 /* Find or create work queue entry for this table */
4926 tab = ATGetQueueEntry(wqueue, rel);
4927
4928 /*
4929 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4930 * partitions that are pending detach.
4931 */
4932 if (rel->rd_rel->relispartition &&
4935 ereport(ERROR,
4937 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4939 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4940
4941 /*
4942 * Copy the original subcommand for each table, so we can scribble on it.
4943 * This avoids conflicts when different child tables need to make
4944 * different parse transformations (for example, the same column may have
4945 * different column numbers in different children).
4946 */
4947 cmd = copyObject(cmd);
4948
4949 /*
4950 * Do permissions and relkind checking, recursion to child tables if
4951 * needed, and any additional phase-1 processing needed. (But beware of
4952 * adding any processing that looks at table details that another
4953 * subcommand could change. In some cases we reject multiple subcommands
4954 * that could try to change the same state in contrary ways.)
4955 */
4956 switch (cmd->subtype)
4957 {
4958 case AT_AddColumn: /* ADD COLUMN */
4959 ATSimplePermissions(cmd->subtype, rel,
4962 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4963 lockmode, context);
4964 /* Recursion occurs during execution phase */
4966 break;
4967 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4969 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4970 lockmode, context);
4971 /* Recursion occurs during execution phase */
4973 break;
4974 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4975
4976 /*
4977 * We allow defaults on views so that INSERT into a view can have
4978 * default-ish behavior. This works because the rewriter
4979 * substitutes default values into INSERTs before it expands
4980 * rules.
4981 */
4982 ATSimplePermissions(cmd->subtype, rel,
4985 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
4986 /* No command-specific prep needed */
4988 break;
4989 case AT_CookedColumnDefault: /* add a pre-cooked default */
4990 /* This is currently used only in CREATE TABLE */
4991 /* (so the permission check really isn't necessary) */
4992 ATSimplePermissions(cmd->subtype, rel,
4994 /* This command never recurses */
4996 break;
4997 case AT_AddIdentity:
4998 ATSimplePermissions(cmd->subtype, rel,
5001 /* Set up recursion for phase 2; no other prep needed */
5002 if (recurse)
5003 cmd->recurse = true;
5005 break;
5006 case AT_SetIdentity:
5007 ATSimplePermissions(cmd->subtype, rel,
5010 /* Set up recursion for phase 2; no other prep needed */
5011 if (recurse)
5012 cmd->recurse = true;
5013 /* This should run after AddIdentity, so do it in MISC pass */
5015 break;
5016 case AT_DropIdentity:
5017 ATSimplePermissions(cmd->subtype, rel,
5020 /* Set up recursion for phase 2; no other prep needed */
5021 if (recurse)
5022 cmd->recurse = true;
5024 break;
5025 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5026 ATSimplePermissions(cmd->subtype, rel,
5028 /* Set up recursion for phase 2; no other prep needed */
5029 if (recurse)
5030 cmd->recurse = true;
5032 break;
5033 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5034 ATSimplePermissions(cmd->subtype, rel,
5036 /* Set up recursion for phase 2; no other prep needed */
5037 if (recurse)
5038 cmd->recurse = true;
5040 break;
5041 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5042 ATSimplePermissions(cmd->subtype, rel,
5044 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5046 break;
5047 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5048 ATSimplePermissions(cmd->subtype, rel,
5050 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5051 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5053 break;
5054 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5055 ATSimplePermissions(cmd->subtype, rel,
5058 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5059 /* No command-specific prep needed */
5061 break;
5062 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5063 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5064 ATSimplePermissions(cmd->subtype, rel,
5067 /* This command never recurses */
5069 break;
5070 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5071 ATSimplePermissions(cmd->subtype, rel,
5074 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5075 /* No command-specific prep needed */
5077 break;
5078 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5079 ATSimplePermissions(cmd->subtype, rel,
5081 /* This command never recurses */
5082 /* No command-specific prep needed */
5084 break;
5085 case AT_DropColumn: /* DROP COLUMN */
5086 ATSimplePermissions(cmd->subtype, rel,
5089 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5090 lockmode, context);
5091 /* Recursion occurs during execution phase */
5093 break;
5094 case AT_AddIndex: /* ADD INDEX */
5096 /* This command never recurses */
5097 /* No command-specific prep needed */
5099 break;
5100 case AT_AddConstraint: /* ADD CONSTRAINT */
5101 ATSimplePermissions(cmd->subtype, rel,
5103 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5104 if (recurse)
5105 {
5106 /* recurses at exec time; lock descendants and set flag */
5107 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5108 cmd->recurse = true;
5109 }
5111 break;
5112 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5114 /* This command never recurses */
5115 /* No command-specific prep needed */
5117 break;
5118 case AT_DropConstraint: /* DROP CONSTRAINT */
5119 ATSimplePermissions(cmd->subtype, rel,
5121 ATCheckPartitionsNotInUse(rel, lockmode);
5122 /* Other recursion occurs during execution phase */
5123 /* No command-specific prep needed except saving recurse flag */
5124 if (recurse)
5125 cmd->recurse = true;
5127 break;
5128 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5129 ATSimplePermissions(cmd->subtype, rel,
5132 /* See comments for ATPrepAlterColumnType */
5133 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5134 AT_PASS_UNSET, context);
5135 Assert(cmd != NULL);
5136 /* Performs own recursion */
5137 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5138 lockmode, context);
5140 break;
5143 /* This command never recurses */
5144 /* No command-specific prep needed */
5146 break;
5147 case AT_ChangeOwner: /* ALTER OWNER */
5148 /* This command never recurses */
5149 /* No command-specific prep needed */
5151 break;
5152 case AT_ClusterOn: /* CLUSTER ON */
5153 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5154 ATSimplePermissions(cmd->subtype, rel,
5156 /* These commands never recurse */
5157 /* No command-specific prep needed */
5159 break;
5160 case AT_SetLogged: /* SET LOGGED */
5161 case AT_SetUnLogged: /* SET UNLOGGED */
5163 if (tab->chgPersistence)
5164 ereport(ERROR,
5166 errmsg("cannot change persistence setting twice")));
5167 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5169 break;
5170 case AT_DropOids: /* SET WITHOUT OIDS */
5171 ATSimplePermissions(cmd->subtype, rel,
5174 break;
5175 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5176 ATSimplePermissions(cmd->subtype, rel,
5178
5179 /* check if another access method change was already requested */
5180 if (tab->chgAccessMethod)
5181 ereport(ERROR,
5183 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5184
5185 ATPrepSetAccessMethod(tab, rel, cmd->name);
5186 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5187 break;
5188 case AT_SetTableSpace: /* SET TABLESPACE */
5191 /* This command never recurses */
5192 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5193 pass = AT_PASS_MISC; /* doesn't actually matter */
5194 break;
5195 case AT_SetRelOptions: /* SET (...) */
5196 case AT_ResetRelOptions: /* RESET (...) */
5197 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5198 ATSimplePermissions(cmd->subtype, rel,
5201 /* This command never recurses */
5202 /* No command-specific prep needed */
5204 break;
5205 case AT_AddInherit: /* INHERIT */
5206 ATSimplePermissions(cmd->subtype, rel,
5208 /* This command never recurses */
5209 ATPrepAddInherit(rel);
5211 break;
5212 case AT_DropInherit: /* NO INHERIT */
5213 ATSimplePermissions(cmd->subtype, rel,
5215 /* This command never recurses */
5216 /* No command-specific prep needed */
5218 break;
5219 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5220 ATSimplePermissions(cmd->subtype, rel,
5222 /* Recursion occurs during execution phase */
5223 if (recurse)
5224 cmd->recurse = true;
5226 break;
5227 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5228 ATSimplePermissions(cmd->subtype, rel,
5230 /* Recursion occurs during execution phase */
5231 /* No command-specific prep needed except saving recurse flag */
5232 if (recurse)
5233 cmd->recurse = true;
5235 break;
5236 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5237 ATSimplePermissions(cmd->subtype, rel,
5240 /* This command never recurses */
5241 /* No command-specific prep needed */
5242 break;
5243 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5246 case AT_EnableTrigAll:
5247 case AT_EnableTrigUser:
5248 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5249 case AT_DisableTrigAll:
5250 case AT_DisableTrigUser:
5251 ATSimplePermissions(cmd->subtype, rel,
5253 /* Set up recursion for phase 2; no other prep needed */
5254 if (recurse)
5255 cmd->recurse = true;
5257 break;
5258 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5261 case AT_DisableRule:
5262 case AT_AddOf: /* OF */
5263 case AT_DropOf: /* NOT OF */
5268 ATSimplePermissions(cmd->subtype, rel,
5270 /* These commands never recurse */
5271 /* No command-specific prep needed */
5273 break;
5274 case AT_GenericOptions:
5276 /* No command-specific prep needed */
5278 break;
5279 case AT_AttachPartition:
5280 ATSimplePermissions(cmd->subtype, rel,
5282 /* No command-specific prep needed */
5284 break;
5285 case AT_DetachPartition:
5287 /* No command-specific prep needed */
5289 break;
5292 /* No command-specific prep needed */
5294 break;
5295 case AT_MergePartitions:
5296 case AT_SplitPartition:
5298 /* No command-specific prep needed */
5300 break;
5301 default: /* oops */
5302 elog(ERROR, "unrecognized alter table type: %d",
5303 (int) cmd->subtype);
5304 pass = AT_PASS_UNSET; /* keep compiler quiet */
5305 break;
5306 }
5308
5309 /* Add the subcommand to the appropriate list for phase 2 */
5310 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5311}
bool PartitionHasPendingDetach(Oid partoid)
#define ATT_SEQUENCE
Definition tablecmds.c:336
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition tablecmds.c:6900
#define ATT_INDEX
Definition tablecmds.c:332
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition tablecmds.c:8782
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:9282
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:7232
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:6855
#define ATT_PARTITIONED_INDEX
Definition tablecmds.c:335
static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:9525
#define ATT_VIEW
Definition tablecmds.c:330
static void ATPrepAddInherit(Relation child_rel)
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
#define ATT_COMPOSITE_TYPE
Definition tablecmds.c:333
#define ATT_MATVIEW
Definition tablecmds.c:331
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)

References Assert, AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AddInherit, AT_AddOf, AT_AlterColumnGenericOptions, AT_AlterColumnType, AT_AlterConstraint, AT_AttachPartition, AT_ChangeOwner, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_MergePartitions, AT_NoForceRowSecurity, AT_PASS_ADD_COL, AT_PASS_ADD_CONSTR, AT_PASS_ADD_INDEX, AT_PASS_ADD_INDEXCONSTR, AT_PASS_ADD_OTHERCONSTR, AT_PASS_ALTER_TYPE, AT_PASS_COL_ATTRS, AT_PASS_DROP, AT_PASS_MISC, AT_PASS_SET_EXPRESSION, AT_PASS_UNSET, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, AT_SplitPartition, AT_ValidateConstraint, ATCheckPartitionsNotInUse(), ATGetQueueEntry(), ATParseTransformCmd(), ATPrepAddColumn(), ATPrepAddInherit(), ATPrepAddPrimaryKey(), ATPrepAlterColumnType(), ATPrepChangePersistence(), ATPrepDropColumn(), ATPrepDropExpression(), ATPrepSetAccessMethod(), ATPrepSetTableSpace(), ATSimplePermissions(), ATSimpleRecursion(), ATT_COMPOSITE_TYPE, ATT_FOREIGN_TABLE, ATT_INDEX, ATT_MATVIEW, ATT_PARTITIONED_INDEX, ATT_PARTITIONED_TABLE, ATT_SEQUENCE, ATT_TABLE, ATT_VIEW, AlteredTableInfo::chgAccessMethod, AlteredTableInfo::chgPersistence, copyObject, AlterTableCmd::def, elog, ereport, errcode(), errhint(), errmsg(), ERROR, fb(), find_all_inheritors(), lappend(), AlterTableCmd::name, PartitionHasPendingDetach(), RelationData::rd_rel, AlterTableCmd::recurse, RelationGetRelationName, RelationGetRelid, AlteredTableInfo::subcmds, and AlterTableCmd::subtype.

Referenced by ATController(), ATPrepAddPrimaryKey(), ATPrepAlterColumnType(), ATSimpleRecursion(), and ATTypedTableRecursion().

◆ ATPrepDropColumn()

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

Definition at line 9282 of file tablecmds.c.

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

References ATTypedTableRecursion(), ereport, errcode(), errmsg(), ERROR, fb(), 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 8782 of file tablecmds.c.

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

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

Referenced by ATPrepCmd().

◆ ATPrepSetAccessMethod()

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

Definition at line 16518 of file tablecmds.c.

16519{
16520 Oid amoid;
16521
16522 /*
16523 * Look up the access method name and check that it differs from the
16524 * table's current AM. If DEFAULT was specified for a partitioned table
16525 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16526 */
16527 if (amname != NULL)
16528 amoid = get_table_am_oid(amname, false);
16529 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16530 amoid = InvalidOid;
16531 else
16533
16534 /* if it's a match, phase 3 doesn't need to do anything */
16535 if (rel->rd_rel->relam == amoid)
16536 return;
16537
16538 /* Save info for Phase 3 to do the real work */
16540 tab->newAccessMethod = amoid;
16541 tab->chgAccessMethod = true;
16542}
Oid get_table_am_oid(const char *amname, bool missing_ok)
Definition amcmds.c:173
#define AT_REWRITE_ACCESS_METHOD
char * default_table_access_method
Definition tableam.c:49

References AT_REWRITE_ACCESS_METHOD, AlteredTableInfo::chgAccessMethod, default_table_access_method, fb(), get_table_am_oid(), InvalidOid, AlteredTableInfo::newAccessMethod, RelationData::rd_rel, and AlteredTableInfo::rewrite.

Referenced by ATPrepCmd().

◆ ATPrepSetTableSpace()

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

Definition at line 16642 of file tablecmds.c.

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

References ACL_CREATE, aclcheck_error(), ACLCHECK_OK, ereport, errcode(), errmsg(), ERROR, fb(), 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 5321 of file tablecmds.c.

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

References AlterTableCreateToastTable(), AT_NUM_PASSES, AT_PASS_ALTER_TYPE, AT_PASS_SET_EXPRESSION, ATExecCmd(), ATPostAlterTypeCleanup(), fb(), lfirst, lfirst_node, NIL, NoLock, AlteredTableInfo::partition_constraint, AlteredTableInfo::rel, relation_close(), relation_open(), AlteredTableInfo::relid, AlteredTableInfo::relkind, and AlteredTableInfo::subcmds.

Referenced by ATController().

◆ ATRewriteTable()

static void ATRewriteTable ( AlteredTableInfo tab,
Oid  OIDNewHeap 
)
static

Definition at line 6161 of file tablecmds.c.

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

References AccessExclusiveLock, Assert, CompactAttribute::attisdropped, CompactAttribute::attnullability, ATTNULLABLE_VALID, attnum, CHECK_FOR_INTERRUPTS, CheckRelationOidLockedByMe(), CONSTR_CHECK, CONSTR_FOREIGN, CONSTR_NOTNULL, AlteredTableInfo::constraints, NewConstraint::contype, CreateExecutorState(), DEBUG1, ExprContext::ecxt_scantuple, elog, ereport, errcode(), errmsg(), errmsg_internal(), ERROR, errtable(), errtablecol(), errtableconstraint(), EState::es_instrument, EState::es_query_cxt, ExecCheck(), ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecEvalExpr(), ExecInitExpr(), ExecPrepareExpr(), ExecRelGenVirtualNotNull(), ExecStoreAllNullTuple(), ExecStoreVirtualTuple(), expand_generated_columns_in_expr(), fb(), foreach_int, ForwardScanDirection, FreeBulkInsertState(), FreeExecutorState(), GetBulkInsertState(), GetCurrentCommandId(), GetLatestSnapshot(), GetPerTupleExprContext, GetPerTupleMemoryContext, i, InitResultRelInfo(), InvalidAttrNumber, lappend_int(), lfirst, lfirst_int, makeNode, MakeSingleTupleTableSlot(), MemoryContextSwitchTo(), NewConstraint::name, NameStr, 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(), TupleDescAttr(), TupleDescCompactAttr(), UnregisterSnapshot(), AlteredTableInfo::validate_default, and AlteredTableInfo::verify_new_notnull.

Referenced by ATRewriteTables().

◆ ATRewriteTables()

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

Definition at line 5873 of file tablecmds.c.

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

References AlteredTableInfo::afterStmts, ATExecSetTableSpace(), ATRewriteTable(), AlteredTableInfo::chgAccessMethod, AlteredTableInfo::chgPersistence, CommandCounterIncrement(), NewConstraint::conid, CONSTR_FOREIGN, AlteredTableInfo::constraints, NewConstraint::contype, NewConstraint::conwithperiod, ereport, errcode(), errmsg(), ERROR, EventTriggerTableRewrite(), fb(), find_composite_type_dependencies(), finish_heap_swap(), getOwnedSequences(), 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 6778 of file tablecmds.c.

6779{
6780 int actual_target;
6781
6782 switch (rel->rd_rel->relkind)
6783 {
6784 case RELKIND_RELATION:
6786 break;
6789 break;
6790 case RELKIND_VIEW:
6792 break;
6793 case RELKIND_MATVIEW:
6795 break;
6796 case RELKIND_INDEX:
6798 break;
6801 break;
6804 break;
6807 break;
6808 case RELKIND_SEQUENCE:
6810 break;
6811 default:
6812 actual_target = 0;
6813 break;
6814 }
6815
6816 /* Wrong target type? */
6817 if ((actual_target & allowed_targets) == 0)
6818 {
6819 const char *action_str = alter_table_type_to_string(cmdtype);
6820
6821 if (action_str)
6822 ereport(ERROR,
6824 /* translator: %s is a group of some SQL keywords */
6825 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6827 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6828 else
6829 /* internal error? */
6830 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6832 }
6833
6834 /* Permissions checks */
6838
6840 ereport(ERROR,
6842 errmsg("permission denied: \"%s\" is a system catalog",
6844}
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition tablecmds.c:6631

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, alter_table_type_to_string(), ATT_COMPOSITE_TYPE, ATT_FOREIGN_TABLE, ATT_INDEX, ATT_MATVIEW, ATT_PARTITIONED_INDEX, ATT_PARTITIONED_TABLE, ATT_SEQUENCE, ATT_TABLE, ATT_VIEW, elog, ereport, errcode(), errdetail_relkind_not_supported(), errmsg(), ERROR, fb(), get_relkind_objtype(), GetUserId(), IsSystemRelation(), object_ownercheck(), RelationData::rd_rel, RelationGetRelationName, and RelationGetRelid.

Referenced by ATAddCheckNNConstraint(), ATExecAddColumn(), ATExecAddInherit(), ATExecAttachPartition(), ATExecDropColumn(), ATExecSetNotNull(), ATPrepCmd(), and dropconstraint_internal().

◆ ATSimpleRecursion()

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

Definition at line 6855 of file tablecmds.c.

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

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

Referenced by ATPrepCmd().

◆ AttachPartitionEnsureIndexes()

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

Definition at line 20614 of file tablecmds.c.

20615{
20616 List *idxes;
20620 ListCell *cell;
20621 MemoryContext cxt;
20623
20625 "AttachPartitionEnsureIndexes",
20628
20633
20634 /* Build arrays of all existing indexes and their IndexInfos */
20636 {
20638
20641 }
20642
20643 /*
20644 * If we're attaching a foreign table, we must fail if any of the indexes
20645 * is a constraint index; otherwise, there's nothing to do here. Do this
20646 * before starting work, to avoid wasting the effort of building a few
20647 * non-unique indexes before coming across a unique one.
20648 */
20649 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20650 {
20651 foreach(cell, idxes)
20652 {
20653 Oid idx = lfirst_oid(cell);
20655
20656 if (idxRel->rd_index->indisunique ||
20657 idxRel->rd_index->indisprimary)
20658 ereport(ERROR,
20660 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20663 errdetail("Partitioned table \"%s\" contains unique indexes.",
20666 }
20667
20668 goto out;
20669 }
20670
20671 /*
20672 * For each index on the partitioned table, find a matching one in the
20673 * partition-to-be; if one is not found, create one.
20674 */
20675 foreach(cell, idxes)
20676 {
20677 Oid idx = lfirst_oid(cell);
20679 IndexInfo *info;
20680 AttrMap *attmap;
20681 bool found = false;
20683
20684 /*
20685 * Ignore indexes in the partitioned table other than partitioned
20686 * indexes.
20687 */
20688 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20689 {
20691 continue;
20692 }
20693
20694 /* construct an indexinfo to compare existing indexes against */
20695 info = BuildIndexInfo(idxRel);
20697 RelationGetDescr(rel),
20698 false);
20700
20701 /*
20702 * Scan the list of existing indexes in the partition-to-be, and mark
20703 * the first matching, valid, unattached one we find, if any, as
20704 * partition of the parent index. If we find one, we're done.
20705 */
20706 for (int i = 0; i < list_length(attachRelIdxs); i++)
20707 {
20710
20711 /* does this index have a parent? if so, can't use it */
20712 if (attachrelIdxRels[i]->rd_rel->relispartition)
20713 continue;
20714
20715 /* If this index is invalid, can't use it */
20716 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20717 continue;
20718
20719 if (CompareIndexInfo(attachInfos[i], info,
20720 attachrelIdxRels[i]->rd_indcollation,
20721 idxRel->rd_indcollation,
20722 attachrelIdxRels[i]->rd_opfamily,
20723 idxRel->rd_opfamily,
20724 attmap))
20725 {
20726 /*
20727 * If this index is being created in the parent because of a
20728 * constraint, then the child needs to have a constraint also,
20729 * so look for one. If there is no such constraint, this
20730 * index is no good, so keep looking.
20731 */
20733 {
20734 cldConstrOid =
20736 cldIdxId);
20737 /* no dice */
20739 continue;
20740
20741 /* Ensure they're both the same type of constraint */
20744 continue;
20745 }
20746
20747 /* bingo. */
20752 found = true;
20753
20755 break;
20756 }
20757 }
20758
20759 /*
20760 * If no suitable index was found in the partition-to-be, create one
20761 * now. Note that if this is a PK, not-null constraints must already
20762 * exist.
20763 */
20764 if (!found)
20765 {
20766 IndexStmt *stmt;
20767 Oid conOid;
20768
20770 idxRel, attmap,
20771 &conOid);
20775 conOid,
20776 -1,
20777 true, false, false, false, false);
20778 }
20779
20781 }
20782
20783out:
20784 /* Clean up. */
20785 for (int i = 0; i < list_length(attachRelIdxs); i++)
20789}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:262
char get_constraint_type(Oid conoid)
Definition lsyscache.c:1219
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
#define foreach_current_index(var_or_cell)
Definition pg_list.h:403

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

Referenced by attachPartitionTable().

◆ AttachPartitionForeignKey()

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

Definition at line 11817 of file tablecmds.c.

11824{
11829 bool queueValidation;
11833
11834 /* Fetch the parent constraint tuple */
11838 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11840 parentConstrIsEnforced = parentConstr->conenforced;
11841
11842 /* Fetch the child constraint tuple */
11846 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11848 partConstrFrelid = partConstr->confrelid;
11849 partConstrRelid = partConstr->conrelid;
11850
11851 /*
11852 * If the referenced table is partitioned, then the partition we're
11853 * attaching now has extra pg_constraint rows and action triggers that are
11854 * no longer needed. Remove those.
11855 */
11857 {
11859
11862
11864 }
11865
11866 /*
11867 * Will we need to validate this constraint? A valid parent constraint
11868 * implies that all child constraints have been validated, so if this one
11869 * isn't, we must trigger phase 3 validation.
11870 */
11871 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11872
11875
11876 /*
11877 * The action triggers in the new partition become redundant -- the parent
11878 * table already has equivalent ones, and those will be able to reach the
11879 * partition. Remove the ones in the partition. We identify them because
11880 * they have our constraint OID, as well as being on the referenced rel.
11881 */
11884
11887
11888 /*
11889 * Like the constraint, attach partition's "check" triggers to the
11890 * corresponding parent triggers if the constraint is ENFORCED. NOT
11891 * ENFORCED constraints do not have these triggers.
11892 */
11894 {
11897
11907 }
11908
11909 /*
11910 * We updated this pg_constraint row above to set its parent; validating
11911 * it will cause its convalidated flag to change, so we need CCI here. In
11912 * addition, we need it unconditionally for the rare case where the parent
11913 * table has *two* identical constraints; when reaching this function for
11914 * the second one, we must have made our changes visible, otherwise we
11915 * would try to attach both to this one.
11916 */
11918
11919 /* If validation is needed, put it in the queue now. */
11920 if (queueValidation)
11921 {
11923 Oid confrelid;
11924
11926
11929 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11930
11931 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11932
11933 /* Use the same lock as for AT_ValidateConstraint */
11938 }
11939}
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
void TriggerSetParentTrigger(Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
Definition trigger.c:1220

References Assert, CommandCounterIncrement(), ConstraintSetParentConstraint(), DropForeignKeyConstraintTriggers(), elog, ERROR, fb(), Form_pg_constraint, get_rel_relkind(), GetForeignKeyCheckTriggers(), GETSTRUCT(), HeapTupleIsValid, ObjectIdGetDatum(), OidIsValid, QueueFKConstraintValidation(), RelationGetRelid, ReleaseSysCache(), RemoveInheritedConstraint(), RowExclusiveLock, RowShareLock, SearchSysCache1(), ShareUpdateExclusiveLock, table_close(), table_open(), and TriggerSetParentTrigger().

Referenced by tryAttachPartitionForeignKey().

◆ attachPartitionTable()

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

Definition at line 20279 of file tablecmds.c.

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

References AttachPartitionEnsureIndexes(), CloneForeignKeyConstraints(), CloneRowTriggersToPartition(), CreateInheritance(), fb(), 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 6930 of file tablecmds.c.

6932{
6933 ListCell *child;
6934 List *children;
6935
6936 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6937
6938 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6940 cmd->behavior);
6941
6942 foreach(child, children)
6943 {
6944 Oid childrelid = lfirst_oid(child);
6946
6947 childrel = relation_open(childrelid, lockmode);
6949 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6951 }
6952}
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition tablecmds.c:7133

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

1387{
1388 int natts;
1390 ListCell *l;
1391 TupleDesc desc;
1392 char *attname;
1393 Oid atttypid;
1394 int32 atttypmod;
1395 Oid attcollation;
1396 int attdim;
1397
1398 /*
1399 * allocate a new tuple descriptor
1400 */
1401 natts = list_length(columns);
1402 desc = CreateTemplateTupleDesc(natts);
1403
1404 attnum = 0;
1405
1406 foreach(l, columns)
1407 {
1408 ColumnDef *entry = lfirst(l);
1411
1412 /*
1413 * for each entry in the list, get the name and type information from
1414 * the list and have TupleDescInitEntry fill in the attribute
1415 * information we need.
1416 */
1417 attnum++;
1418
1419 attname = entry->colname;
1420 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1421
1423 if (aclresult != ACLCHECK_OK)
1425
1426 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1428 if (attdim > PG_INT16_MAX)
1429 ereport(ERROR,
1431 errmsg("too many array dimensions"));
1432
1433 if (entry->typeName->setof)
1434 ereport(ERROR,
1436 errmsg("column \"%s\" cannot be declared SETOF",
1437 attname)));
1438
1440 atttypid, atttypmod, attdim);
1441 att = TupleDescAttr(desc, attnum - 1);
1442
1443 /* Override TupleDescInitEntry's settings as requested */
1444 TupleDescInitEntryCollation(desc, attnum, attcollation);
1445
1446 /* Fill in additional stuff not handled by TupleDescInitEntry */
1447 att->attnotnull = entry->is_not_null;
1448 att->attislocal = entry->is_local;
1449 att->attinhcount = entry->inhcount;
1450 att->attidentity = entry->identity;
1451 att->attgenerated = entry->generated;
1452 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1453 if (entry->storage)
1454 att->attstorage = entry->storage;
1455 else if (entry->storage_name)
1456 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1457
1459 }
1460
1461 return desc;
1462}
NameData attname
bool is_not_null
Definition parsenodes.h:759
char identity
Definition parsenodes.h:765
char * storage_name
Definition parsenodes.h:762
char * colname
Definition parsenodes.h:754
TypeName * typeName
Definition parsenodes.h:755
char generated
Definition parsenodes.h:768
char storage
Definition parsenodes.h:761
bool is_local
Definition parsenodes.h:758
int16 inhcount
Definition parsenodes.h:757
char * compression
Definition parsenodes.h:756
bool setof
Definition parsenodes.h:287
TupleDesc CreateTemplateTupleDesc(int natts)
Definition tupdesc.c:165
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition tupdesc.c:100
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
Definition tupdesc.c:1009
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition tupdesc.c:825

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

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

◆ buildExpressionExecutionStates()

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

Definition at line 22112 of file tablecmds.c.

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

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

Referenced by MergePartitionsMoveRows(), and SplitPartitionMoveRows().

◆ change_owner_fix_column_acls()

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

Definition at line 16340 of file tablecmds.c.

16341{
16343 SysScanDesc scan;
16344 ScanKeyData key[1];
16346
16348 ScanKeyInit(&key[0],
16351 ObjectIdGetDatum(relationOid));
16353 true, NULL, 1, key);
16355 {
16360 Acl *newAcl;
16362 bool isNull;
16363 HeapTuple newtuple;
16364
16365 /* Ignore dropped columns */
16366 if (att->attisdropped)
16367 continue;
16368
16372 &isNull);
16373 /* Null ACLs do not require changes */
16374 if (isNull)
16375 continue;
16376
16377 memset(repl_null, false, sizeof(repl_null));
16378 memset(repl_repl, false, sizeof(repl_repl));
16379
16384
16388
16389 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16390
16391 heap_freetuple(newtuple);
16392 }
16393 systable_endscan(scan);
16395}

References aclnewowner(), BTEqualStrategyNumber, CatalogTupleUpdate(), DatumGetAclP, fb(), GETSTRUCT(), heap_freetuple(), heap_getattr(), heap_modify_tuple(), HeapTupleIsValid, 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 16405 of file tablecmds.c.

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

References AccessShareLock, ATExecChangeOwner(), BTEqualStrategyNumber, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, fb(), Form_pg_depend, GETSTRUCT(), HeapTupleIsValid, 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 7685 of file tablecmds.c.

7687{
7689 int attnum;
7690
7691 /*
7692 * this test is deliberately not attisdropped-aware, since if one tries to
7693 * add a column matching a dropped column name, it's gonna fail anyway.
7694 */
7697 PointerGetDatum(colname));
7699 return true;
7700
7703
7704 /*
7705 * We throw a different error message for conflicts with system column
7706 * names, since they are normally not shown and the user might otherwise
7707 * be confused about the reason for the conflict.
7708 */
7709 if (attnum <= 0)
7710 ereport(ERROR,
7712 errmsg("column name \"%s\" conflicts with a system column name",
7713 colname)));
7714 else
7715 {
7716 if (if_not_exists)
7717 {
7720 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7721 colname, RelationGetRelationName(rel))));
7722 return false;
7723 }
7724
7725 ereport(ERROR,
7727 errmsg("column \"%s\" of relation \"%s\" already exists",
7728 colname, RelationGetRelationName(rel))));
7729 }
7730
7731 return true;
7732}
HeapTuple SearchSysCache2(SysCacheIdentifier cacheId, Datum key1, Datum key2)
Definition syscache.c:230

References attnum, ereport, errcode(), errmsg(), ERROR, fb(), 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 7182 of file tablecmds.c.

7183{
7185 bool typeOk = false;
7186
7187 if (typ->typtype == TYPTYPE_COMPOSITE)
7188 {
7190
7191 Assert(OidIsValid(typ->typrelid));
7193 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7194
7195 /*
7196 * Close the parent rel, but keep our AccessShareLock on it until xact
7197 * commit. That will prevent someone else from deleting or ALTERing
7198 * the type before the typed table creation/conversion commits.
7199 */
7201
7202 if (!typeOk)
7203 ereport(ERROR,
7205 errmsg("type %s is the row type of another table",
7206 format_type_be(typ->oid)),
7207 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7208 }
7209 else
7210 ereport(ERROR,
7212 errmsg("type %s is not a composite type",
7213 format_type_be(typ->oid))));
7214}

References AccessShareLock, Assert, ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), Form_pg_type, format_type_be(), GETSTRUCT(), NoLock, OidIsValid, relation_close(), and relation_open().

Referenced by ATExecAddOf(), and transformOfType().

◆ CheckAlterTableIsSafe()

static void CheckAlterTableIsSafe ( Relation  rel)
static

Definition at line 4457 of file tablecmds.c.

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

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

Referenced by addFkRecurseReferencing(), AlterTable(), ATAddCheckNNConstraint(), ATCheckPartitionsNotInUse(), ATExecAddColumn(), ATExecDropColumn(), ATPrepAlterColumnType(), ATSimpleRecursion(), ATTypedTableRecursion(), dropconstraint_internal(), and set_attnotnull().

◆ checkFkeyPermissions()

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

Definition at line 13692 of file tablecmds.c.

13693{
13694 Oid roleid = GetUserId();
13696 int i;
13697
13698 /* Okay if we have relation-level REFERENCES permission */
13701 if (aclresult == ACLCHECK_OK)
13702 return;
13703 /* Else we must have REFERENCES on each column */
13704 for (i = 0; i < natts; i++)
13705 {
13707 roleid, ACL_REFERENCES);
13708 if (aclresult != ACLCHECK_OK)
13711 }
13712}
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition aclchk.c:3886
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4057
#define ACL_REFERENCES
Definition parsenodes.h:81

References ACL_REFERENCES, aclcheck_error(), ACLCHECK_OK, fb(), 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 3701 of file tablecmds.c.

3702{
3704
3705 /*
3706 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3707 * stored as 0.
3708 */
3709 oldTableSpaceId = rel->rd_rel->reltablespace;
3712 return false;
3713
3714 /*
3715 * We cannot support moving mapped relations into different tablespaces.
3716 * (In particular this eliminates all shared catalogs.)
3717 */
3718 if (RelationIsMapped(rel))
3719 ereport(ERROR,
3721 errmsg("cannot move system relation \"%s\"",
3723
3724 /* Cannot move a non-shared relation into pg_global */
3726 ereport(ERROR,
3728 errmsg("only shared relations can be placed in pg_global tablespace")));
3729
3730 /*
3731 * Do not allow moving temp tables of other backends ... their local
3732 * buffer manager is not going to cope.
3733 */
3734 if (RELATION_IS_OTHER_TEMP(rel))
3735 ereport(ERROR,
3737 errmsg("cannot move temporary tables of other sessions")));
3738
3739 return true;
3740}
#define RelationIsMapped(relation)
Definition rel.h:563

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

4425{
4426 int expected_refcnt;
4427
4428 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4429 if (rel->rd_refcnt != expected_refcnt)
4430 ereport(ERROR,
4432 /* translator: first %s is a SQL command, eg ALTER TABLE */
4433 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4435
4436 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4437 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4439 ereport(ERROR,
4441 /* translator: first %s is a SQL command, eg ALTER TABLE */
4442 errmsg("cannot %s \"%s\" because it has pending trigger events",
4444}
int rd_refcnt
Definition rel.h:59
bool rd_isnailed
Definition rel.h:62
bool AfterTriggerPendingOnRel(Oid relid)
Definition trigger.c:6081

References AfterTriggerPendingOnRel(), ereport, errcode(), errmsg(), ERROR, fb(), 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 9897 of file tablecmds.c.

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

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

Referenced by ATExecAddConstraint().

◆ CloneFkReferenced()

static void CloneFkReferenced ( Relation  parentRel,
Relation  partitionRel 
)
static

Definition at line 11273 of file tablecmds.c.

11274{
11276 AttrMap *attmap;
11277 ListCell *cell;
11278 SysScanDesc scan;
11279 ScanKeyData key[2];
11280 HeapTuple tuple;
11281 List *clone = NIL;
11283
11284 /*
11285 * Search for any constraints where this partition's parent is in the
11286 * referenced side. However, we must not clone any constraint whose
11287 * parent constraint is also going to be cloned, to avoid duplicates. So
11288 * do it in two steps: first construct the list of constraints to clone,
11289 * then go over that list cloning those whose parents are not in the list.
11290 * (We must not rely on the parent being seen first, since the catalog
11291 * scan could return children first.)
11292 */
11294 ScanKeyInit(&key[0],
11297 ScanKeyInit(&key[1],
11300 /* This is a seqscan, as we don't have a usable index ... */
11302 NULL, 2, key);
11303 while ((tuple = systable_getnext(scan)) != NULL)
11304 {
11306
11308 }
11309 systable_endscan(scan);
11311
11312 /*
11313 * Triggers of the foreign keys will be manipulated a bunch of times in
11314 * the loop below. To avoid repeatedly opening/closing the trigger
11315 * catalog relation, we open it here and pass it to the subroutines called
11316 * below.
11317 */
11319
11322 false);
11323 foreach(cell, clone)
11324 {
11325 Oid constrOid = lfirst_oid(cell);
11328 Oid indexOid;
11330 int numfks;
11337 int numfkdelsetcols;
11338 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11340 ObjectAddress address;
11343
11345 if (!HeapTupleIsValid(tuple))
11346 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11348
11349 /*
11350 * As explained above: don't try to clone a constraint for which we're
11351 * going to clone the parent.
11352 */
11353 if (list_member_oid(clone, constrForm->conparentid))
11354 {
11355 ReleaseSysCache(tuple);
11356 continue;
11357 }
11358
11359 /* We need the same lock level that CreateTrigger will acquire */
11361
11362 indexOid = constrForm->conindid;
11364 &numfks,
11365 conkey,
11366 confkey,
11367 conpfeqop,
11368 conppeqop,
11369 conffeqop,
11371 confdelsetcols);
11372
11373 for (int i = 0; i < numfks; i++)
11374 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11375
11378 fkconstraint->conname = NameStr(constrForm->conname);
11379 fkconstraint->deferrable = constrForm->condeferrable;
11380 fkconstraint->initdeferred = constrForm->condeferred;
11381 fkconstraint->location = -1;
11382 fkconstraint->pktable = NULL;
11383 /* ->fk_attrs determined below */
11384 fkconstraint->pk_attrs = NIL;
11385 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11386 fkconstraint->fk_upd_action = constrForm->confupdtype;
11387 fkconstraint->fk_del_action = constrForm->confdeltype;
11388 fkconstraint->fk_del_set_cols = NIL;
11389 fkconstraint->old_conpfeqop = NIL;
11390 fkconstraint->old_pktable_oid = InvalidOid;
11391 fkconstraint->is_enforced = constrForm->conenforced;
11392 fkconstraint->skip_validation = false;
11393 fkconstraint->initially_valid = constrForm->convalidated;
11394
11395 /* set up colnames that are used to generate the constraint name */
11396 for (int i = 0; i < numfks; i++)
11397 {
11399
11401 conkey[i] - 1);
11402 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11403 makeString(NameStr(att->attname)));
11404 }
11405
11406 /*
11407 * Add the new foreign key constraint pointing to the new partition.
11408 * Because this new partition appears in the referenced side of the
11409 * constraint, we don't need to set up for Phase 3 check.
11410 */
11412 if (!OidIsValid(partIndexId))
11413 elog(ERROR, "index for %u not found in partition %s",
11415
11416 /*
11417 * Get the "action" triggers belonging to the constraint to pass as
11418 * parent OIDs for similar triggers that will be created on the
11419 * partition in addFkRecurseReferenced().
11420 */
11421 if (constrForm->conenforced)
11423 constrForm->confrelid, constrForm->conrelid,
11425
11426 /* Add this constraint ... */
11428 fkconstraint->conname, fkconstraint, fkRel,
11432 numfkdelsetcols, confdelsetcols, false,
11433 constrForm->conperiod);
11434 /* ... and recurse */
11436 fkRel,
11439 address.objectId,
11440 numfks,
11442 conkey,
11443 conpfeqop,
11444 conppeqop,
11445 conffeqop,
11447 confdelsetcols,
11448 true,
11451 constrForm->conperiod);
11452
11454 ReleaseSysCache(tuple);
11455 }
11456
11458}
void DeconstructFkConstraintRow(HeapTuple tuple, int *numfks, AttrNumber *conkey, AttrNumber *confkey, Oid *pf_eq_oprs, Oid *pp_eq_oprs, Oid *ff_eq_oprs, int *num_fk_del_set_cols, AttrNumber *fk_del_set_cols)
static Datum CharGetDatum(char X)
Definition postgres.h:132
static void GetForeignKeyActionTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *deleteTriggerOid, Oid *updateTriggerOid)

References addFkConstraint(), addFkRecurseReferenced(), addFkReferencedSide, BTEqualStrategyNumber, build_attrmap_by_name(), CharGetDatum(), DeconstructFkConstraintRow(), elog, ERROR, fb(), Form_pg_constraint, GetForeignKeyActionTriggers(), GETSTRUCT(), HeapTupleIsValid, i, index_get_partition(), INDEX_MAX_KEYS, InvalidOid, lappend(), lappend_oid(), lfirst_oid, list_member_oid(), makeNode, makeString(), NameStr, NIL, NoLock, ObjectAddress::objectId, ObjectIdGetDatum(), OidIsValid, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, RowShareLock, ScanKeyInit(), SearchSysCache1(), ShareRowExclusiveLock, 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 11474 of file tablecmds.c.

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

References AccessExclusiveLock, addFkConstraint(), addFkRecurseReferencing(), addFkReferencingSide, build_attrmap_by_name(), copyObject, DeconstructFkConstraintRow(), elog, ereport, errcode(), errmsg(), ERROR, fb(), find_all_inheritors(), Form_pg_constraint, get_constraint_name(), GetForeignKeyCheckTriggers(), GETSTRUCT(), HeapTupleIsValid, i, INDEX_MAX_KEYS, InvalidOid, lappend(), lappend_oid(), lfirst, lfirst_node, lfirst_oid, list_member_oid(), makeNode, makeString(), NameStr, NIL, NoLock, ObjectAddress::objectId, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetDescr, RelationGetFKeyList(), RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), ShareRowExclusiveLock, table_close(), table_open(), tryAttachPartitionForeignKey(), and TupleDescAttr().

Referenced by CloneForeignKeyConstraints().

◆ CloneForeignKeyConstraints()

static void CloneForeignKeyConstraints ( List **  wqueue,
Relation  parentRel,
Relation  partitionRel 
)
static

Definition at line 11244 of file tablecmds.c.

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

References Assert, CloneFkReferenced(), CloneFkReferencing(), and fb().

Referenced by attachPartitionTable(), and DefineRelation().

◆ CloneRowTriggersToPartition()

static void CloneRowTriggersToPartition ( Relation  parent,
Relation  partition 
)
static

Definition at line 20797 of file tablecmds.c.

20798{
20801 SysScanDesc scan;
20802 HeapTuple tuple;
20804
20809 true, NULL, 1, &key);
20810
20812 "clone trig", ALLOCSET_SMALL_SIZES);
20813
20814 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
20815 {
20818 Node *qual = NULL;
20819 Datum value;
20820 bool isnull;
20821 List *cols = NIL;
20822 List *trigargs = NIL;
20824
20825 /*
20826 * Ignore statement-level triggers; those are not cloned.
20827 */
20828 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
20829 continue;
20830
20831 /*
20832 * Don't clone internal triggers, because the constraint cloning code
20833 * will.
20834 */
20835 if (trigForm->tgisinternal)
20836 continue;
20837
20838 /*
20839 * Complain if we find an unexpected trigger type.
20840 */
20841 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
20842 !TRIGGER_FOR_AFTER(trigForm->tgtype))
20843 elog(ERROR, "unexpected trigger \"%s\" found",
20844 NameStr(trigForm->tgname));
20845
20846 /* Use short-lived context for CREATE TRIGGER */
20848
20849 /*
20850 * If there is a WHEN clause, generate a 'cooked' version of it that's
20851 * appropriate for the partition.
20852 */
20854 RelationGetDescr(pg_trigger), &isnull);
20855 if (!isnull)
20856 {
20858 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
20859 partition, parent);
20860 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
20861 partition, parent);
20862 }
20863
20864 /*
20865 * If there is a column list, transform it to a list of column names.
20866 * Note we don't need to map this list in any way ...
20867 */
20868 if (trigForm->tgattr.dim1 > 0)
20869 {
20870 int i;
20871
20872 for (i = 0; i < trigForm->tgattr.dim1; i++)
20873 {
20875
20876 col = TupleDescAttr(parent->rd_att,
20877 trigForm->tgattr.values[i] - 1);
20878 cols = lappend(cols,
20879 makeString(pstrdup(NameStr(col->attname))));
20880 }
20881 }
20882
20883 /* Reconstruct trigger arguments list. */
20884 if (trigForm->tgnargs > 0)
20885 {
20886 char *p;
20887
20889 RelationGetDescr(pg_trigger), &isnull);
20890 if (isnull)
20891 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
20893
20894 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
20895
20896 for (int i = 0; i < trigForm->tgnargs; i++)
20897 {
20899 p += strlen(p) + 1;
20900 }
20901 }
20902
20904 trigStmt->replace = false;
20905 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
20906 trigStmt->trigname = NameStr(trigForm->tgname);
20907 trigStmt->relation = NULL;
20908 trigStmt->funcname = NULL; /* passed separately */
20909 trigStmt->args = trigargs;
20910 trigStmt->row = true;
20911 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
20912 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
20913 trigStmt->columns = cols;
20914 trigStmt->whenClause = NULL; /* passed separately */
20915 trigStmt->transitionRels = NIL; /* not supported at present */
20916 trigStmt->deferrable = trigForm->tgdeferrable;
20917 trigStmt->initdeferred = trigForm->tginitdeferred;
20918 trigStmt->constrrel = NULL; /* passed separately */
20919
20921 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
20922 trigForm->tgfoid, trigForm->oid, qual,
20923 false, true, trigForm->tgenabled);
20924
20927 }
20928
20930
20931 systable_endscan(scan);
20933}
#define TextDatumGetCString(d)
Definition builtins.h:99
#define DatumGetByteaPP(X)
Definition fmgr.h:292
static struct @174 value
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:403
#define ALLOCSET_SMALL_SIZES
Definition memutils.h:170
#define PRS2_OLD_VARNO
Definition primnodes.h:251
#define PRS2_NEW_VARNO
Definition primnodes.h:252
void * stringToNode(const char *str)
Definition read.c:90
ObjectAddress CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
Definition trigger.c:177
static char * VARDATA_ANY(const void *PTR)
Definition varatt.h:486

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, BTEqualStrategyNumber, CreateTriggerFiringOn(), CurrentMemoryContext, DatumGetByteaPP, elog, ERROR, fb(), Form_pg_trigger, GETSTRUCT(), heap_getattr(), HeapTupleIsValid, i, InvalidOid, lappend(), makeNode, makeString(), map_partition_varattnos(), MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), NameStr, NIL, ObjectIdGetDatum(), OidIsValid, PRS2_NEW_VARNO, PRS2_OLD_VARNO, pstrdup(), RelationData::rd_att, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), stringToNode(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TextDatumGetCString, TupleDescAttr(), value, and VARDATA_ANY().

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

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

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

Referenced by DefineRelation().

◆ ConstraintImpliedByRelConstraint()

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

Definition at line 20139 of file tablecmds.c.

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

References Assert, canonicalize_qual(), ConstrCheck::ccbin, ConstrCheck::ccenforced, ConstrCheck::ccvalid, TupleConstr::check, eval_const_expressions(), fb(), 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 17496 of file tablecmds.c.

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

References a, b, decompile_conbin(), fb(), Form_pg_constraint, 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 13821 of file tablecmds.c.

13824{
13827
13828 /*
13829 * Note: for a self-referential FK (referencing and referenced tables are
13830 * the same), it is important that the ON UPDATE action fires before the
13831 * CHECK action, since both triggers will fire on the same row during an
13832 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
13833 * state of the row. Triggers fire in name order, so we ensure this by
13834 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
13835 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
13836 */
13838 fk_trigger->replace = false;
13839 fk_trigger->isconstraint = true;
13840 fk_trigger->trigname = "RI_ConstraintTrigger_c";
13841 fk_trigger->relation = NULL;
13842
13843 /* Either ON INSERT or ON UPDATE */
13844 if (on_insert)
13845 {
13846 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
13848 }
13849 else
13850 {
13851 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
13853 }
13854
13855 fk_trigger->args = NIL;
13856 fk_trigger->row = true;
13858 fk_trigger->columns = NIL;
13859 fk_trigger->whenClause = NULL;
13860 fk_trigger->transitionRels = NIL;
13861 fk_trigger->deferrable = fkconstraint->deferrable;
13862 fk_trigger->initdeferred = fkconstraint->initdeferred;
13863 fk_trigger->constrrel = NULL;
13864
13866 constraintOid, indexOid, InvalidOid,
13867 parentTrigOid, NULL, true, false);
13868
13869 /* Make changes-so-far visible */
13871
13872 return trigAddress.objectId;
13873}
List * SystemFuncName(char *name)
ObjectAddress CreateTrigger(CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition)
Definition trigger.c:160

References CommandCounterIncrement(), CreateTrigger(), fb(), InvalidOid, makeNode, NIL, and SystemFuncName().

Referenced by createForeignKeyCheckTriggers().

◆ createForeignKeyActionTriggers()

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

Definition at line 13884 of file tablecmds.c.

13888{
13891
13892 /*
13893 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13894 * DELETE action on the referenced table.
13895 */
13897 fk_trigger->replace = false;
13898 fk_trigger->isconstraint = true;
13899 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13900 fk_trigger->relation = NULL;
13901 fk_trigger->args = NIL;
13902 fk_trigger->row = true;
13905 fk_trigger->columns = NIL;
13906 fk_trigger->whenClause = NULL;
13907 fk_trigger->transitionRels = NIL;
13908 fk_trigger->constrrel = NULL;
13909
13910 switch (fkconstraint->fk_del_action)
13911 {
13913 fk_trigger->deferrable = fkconstraint->deferrable;
13914 fk_trigger->initdeferred = fkconstraint->initdeferred;
13915 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
13916 break;
13918 fk_trigger->deferrable = false;
13919 fk_trigger->initdeferred = false;
13920 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
13921 break;
13923 fk_trigger->deferrable = false;
13924 fk_trigger->initdeferred = false;
13925 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
13926 break;
13928 fk_trigger->deferrable = false;
13929 fk_trigger->initdeferred = false;
13930 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
13931 break;
13933 fk_trigger->deferrable = false;
13934 fk_trigger->initdeferred = false;
13935 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
13936 break;
13937 default:
13938 elog(ERROR, "unrecognized FK action type: %d",
13939 (int) fkconstraint->fk_del_action);
13940 break;
13941 }
13942
13944 constraintOid, indexOid, InvalidOid,
13945 parentDelTrigger, NULL, true, false);
13946 if (deleteTrigOid)
13947 *deleteTrigOid = trigAddress.objectId;
13948
13949 /* Make changes-so-far visible */
13951
13952 /*
13953 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
13954 * UPDATE action on the referenced table.
13955 */
13957 fk_trigger->replace = false;
13958 fk_trigger->isconstraint = true;
13959 fk_trigger->trigname = "RI_ConstraintTrigger_a";
13960 fk_trigger->relation = NULL;
13961 fk_trigger->args = NIL;
13962 fk_trigger->row = true;
13965 fk_trigger->columns = NIL;
13966 fk_trigger->whenClause = NULL;
13967 fk_trigger->transitionRels = NIL;
13968 fk_trigger->constrrel = NULL;
13969
13970 switch (fkconstraint->fk_upd_action)
13971 {
13973 fk_trigger->deferrable = fkconstraint->deferrable;
13974 fk_trigger->initdeferred = fkconstraint->initdeferred;
13975 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
13976 break;
13978 fk_trigger->deferrable = false;
13979 fk_trigger->initdeferred = false;
13980 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
13981 break;
13983 fk_trigger->deferrable = false;
13984 fk_trigger->initdeferred = false;
13985 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
13986 break;
13988 fk_trigger->deferrable = false;
13989 fk_trigger->initdeferred = false;
13990 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
13991 break;
13993 fk_trigger->deferrable = false;
13994 fk_trigger->initdeferred = false;
13995 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
13996 break;
13997 default:
13998 elog(ERROR, "unrecognized FK action type: %d",
13999 (int) fkconstraint->fk_upd_action);
14000 break;
14001 }
14002
14004 constraintOid, indexOid, InvalidOid,
14005 parentUpdTrigger, NULL, true, false);
14006 if (updateTrigOid)
14007 *updateTrigOid = trigAddress.objectId;
14008}
#define FKCONSTR_ACTION_NOACTION

References CommandCounterIncrement(), CreateTrigger(), elog, ERROR, fb(), FKCONSTR_ACTION_CASCADE, FKCONSTR_ACTION_NOACTION, FKCONSTR_ACTION_RESTRICT, FKCONSTR_ACTION_SETDEFAULT, FKCONSTR_ACTION_SETNULL, InvalidOid, makeNode, NIL, and SystemFuncName().

Referenced by addFkRecurseReferenced(), and ATExecAlterConstrEnforceability().

◆ createForeignKeyCheckTriggers()

static void createForeignKeyCheckTriggers ( Oid  myRelOid,
Oid  refRelOid,
Constraint fkconstraint,
Oid  constraintOid,
Oid  indexOid,
Oid  parentInsTrigger,
Oid  parentUpdTrigger,
Oid insertTrigOid,
Oid updateTrigOid 
)
static

Definition at line 14019 of file tablecmds.c.

14024{
14026 constraintOid, indexOid,
14027 parentInsTrigger, true);
14029 constraintOid, indexOid,
14030 parentUpdTrigger, false);
14031}
static Oid CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)

References CreateFKCheckTrigger(), and fb().

Referenced by addFkRecurseReferencing(), and ATExecAlterConstrEnforceability().

◆ CreateInheritance()

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

Definition at line 17399 of file tablecmds.c.

17400{
17402 SysScanDesc scan;
17406
17407 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17409
17410 /*
17411 * Check for duplicates in the list of parents, and determine the highest
17412 * inhseqno already present; we'll use the next one for the new parent.
17413 * Also, if proposed child is a partition, it cannot already be
17414 * inheriting.
17415 *
17416 * Note: we do not reject the case where the child already inherits from
17417 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17418 */
17419 ScanKeyInit(&key,
17424 true, NULL, 1, &key);
17425
17426 /* inhseqno sequences start at 1 */
17427 inhseqno = 0;
17429 {
17431
17432 if (inh->inhparent == RelationGetRelid(parent_rel))
17433 ereport(ERROR,
17435 errmsg("relation \"%s\" would be inherited from more than once",
17437
17438 if (inh->inhseqno > inhseqno)
17439 inhseqno = inh->inhseqno;
17440 }
17441 systable_endscan(scan);
17442
17443 /* Match up the columns and bump attinhcount as needed */
17445
17446 /* Match up the constraints and bump coninhcount as needed */
17448
17449 /*
17450 * OK, it looks valid. Make the catalog entries that show inheritance.
17451 */
17454 inhseqno + 1,
17456 parent_rel->rd_rel->relkind ==
17458
17459 /* Now we're done with pg_inherits */
17461}
END_CATALOG_STRUCT typedef FormData_pg_inherits * Form_pg_inherits
Definition pg_inherits.h:49
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispartition)
static void StoreCatalogInheritance1(Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
Definition tablecmds.c:3573
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)

References BTEqualStrategyNumber, ereport, errcode(), errmsg(), ERROR, fb(), Form_pg_inherits, GETSTRUCT(), HeapTupleIsValid, MergeAttributesIntoExisting(), MergeConstraintsIntoExisting(), ObjectIdGetDatum(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), StoreCatalogInheritance1(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecAddInherit(), and attachPartitionTable().

◆ createPartitionTable()

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

Definition at line 22455 of file tablecmds.c.

22457{
22459 Oid newRelId;
22462 List *colList = NIL;
22463 Oid relamId;
22467
22468 /* If the existing rel is temp, it must belong to this session. */
22470 ereport(ERROR,
22472 errmsg("cannot create as partition of temporary relation of another session"));
22473
22474 /* Look up inheritance ancestors and generate the relation schema. */
22476
22477 /* Create a tuple descriptor from the relation schema. */
22479
22480 /* Look up the access method for the new relation. */
22482
22483 /* Look up the namespace in which we are supposed to create the relation. */
22484 namespaceId =
22487 ereport(ERROR,
22489 errmsg("relation \"%s\" already exists", newPartName->relname));
22490
22491 /*
22492 * We intended to create the partition with the same persistence as the
22493 * parent table, but we still need to recheck because that might be
22494 * affected by the search_path. If the parent is permanent, so must be
22495 * all of its partitions.
22496 */
22497 if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22498 newPartName->relpersistence == RELPERSISTENCE_TEMP)
22499 ereport(ERROR,
22501 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
22503
22504 /* Permanent rels cannot be partitions belonging to a temporary parent. */
22505 if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
22506 parent_relform->relpersistence == RELPERSISTENCE_TEMP)
22507 ereport(ERROR,
22509 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
22511
22512 /* Create the relation. */
22515 parent_relform->reltablespace,
22516 InvalidOid,
22517 InvalidOid,
22518 InvalidOid,
22519 ownerId,
22520 relamId,
22521 descriptor,
22522 NIL,
22524 newPartName->relpersistence,
22525 false,
22526 false,
22528 (Datum) 0,
22529 true,
22531 true,
22532 InvalidOid,
22533 NULL);
22534
22535 /*
22536 * We must bump the command counter to make the newly-created relation
22537 * tuple visible for opening.
22538 */
22540
22541 /*
22542 * Open the new partition with no lock, because we already have an
22543 * AccessExclusiveLock placed there after creation.
22544 */
22546
22547 /* Find or create a work queue entry for the newly created table. */
22549
22550 /* Create constraints, default values, and generated values. */
22552
22553 /*
22554 * Need to call CommandCounterIncrement, so a fresh relcache entry has
22555 * newly installed constraint info.
22556 */
22558
22559 return newRel;
22560}
Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid reltypeid, Oid reloftypeid, Oid ownerid, Oid accessmtd, TupleDesc tupdesc, List *cooked_constraints, char relkind, char relpersistence, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, bool allow_system_table_mods, bool is_internal, Oid relrewrite, ObjectAddress *typaddress)
Definition heap.c:1122
@ ONCOMMIT_NOOP
Definition primnodes.h:59
static void createTableConstraints(List **wqueue, AlteredTableInfo *tab, Relation parent_rel, Relation newRel)
static List * getAttributesList(Relation parent_rel)

References allowSystemTableMods, ATGetQueueEntry(), BuildDescForRelation(), CommandCounterIncrement(), createTableConstraints(), ereport, errcode(), errmsg(), ERROR, fb(), getAttributesList(), heap_create_with_catalog(), InvalidOid, NIL, NoLock, OidIsValid, ONCOMMIT_NOOP, RangeVarGetAndCheckCreationNamespace(), RELATION_IS_OTHER_TEMP, RelationGetRelationName, and table_open().

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ createSplitPartitionContext()

static SplitPartitionContext * createSplitPartitionContext ( Relation  partRel)
static

Definition at line 22943 of file tablecmds.c.

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

References fb(), GetBulkInsertState(), palloc0_object, and table_slot_create().

Referenced by SplitPartitionMoveRows().

◆ createTableConstraints()

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

Definition at line 22248 of file tablecmds.c.

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

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

Referenced by createPartitionTable().

◆ decompile_conbin()

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

Definition at line 17468 of file tablecmds.c.

17469{
17471 bool isnull;
17472 Datum attr;
17473 Datum expr;
17474
17476 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17477 if (isnull)
17478 elog(ERROR, "null conbin for constraint %u", con->oid);
17479
17480 expr = DirectFunctionCall2(pg_get_expr, attr,
17481 ObjectIdGetDatum(con->conrelid));
17482 return TextDatumGetCString(expr);
17483}
#define DirectFunctionCall2(func, arg1, arg2)
Definition fmgr.h:686
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition ruleutils.c:2675

References DirectFunctionCall2, elog, ERROR, fb(), Form_pg_constraint, GETSTRUCT(), heap_getattr(), ObjectIdGetDatum(), pg_get_expr(), and TextDatumGetCString.

Referenced by constraints_equivalent().

◆ DefineRelation()

ObjectAddress DefineRelation ( CreateStmt stmt,
char  relkind,
Oid  ownerId,
ObjectAddress typaddress,
const char queryString 
)

Definition at line 769 of file tablecmds.c.

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

References AccessExclusiveLock, AccessShareLock, ACL_CREATE, ACL_USAGE, aclcheck_error(), aclcheck_error_type(), ACLCHECK_OK, addNSItemToQuery(), addRangeTableEntryForRelation(), AddRelationNewConstraints(), AddRelationNotNullConstraints(), allowSystemTableMods, Assert, attnum, build_attrmap_by_name(), BuildDescForRelation(), check_default_partition_contents(), check_new_partition_bound(), CloneForeignKeyConstraints(), CloneRowTriggersToPartition(), CommandCounterIncrement(), ComputePartitionAttrs(), CONSTR_DEFAULT, default_table_access_method, DefineIndex(), elog, ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), foreach_int, foreach_ptr, generateClonedIndexStmt(), 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(), InSecurityRestrictedOperation(), InvalidOid, lappend(), lappend_oid(), lfirst, lfirst_oid, linitial_oid, list_concat(), list_free(), list_length(), list_member_oid(), make_parsestate(), MergeAttributes(), MyDatabaseTableSpace, NAMEDATALEN, NIL, NoLock, object_aclcheck(), OBJECT_TABLESPACE, ObjectAddressSet, OidIsValid, ONCOMMIT_NOOP, ParseState::p_sourcetext, palloc_object, PARTITION_MAX_KEYS, partitioned_table_reloptions(), RangeVarGetAndCheckCreationNamespace(), RangeVarGetRelid, RelationData::rd_rel, relation_close(), relation_open(), RelationGetDescr, RelationGetIndexList(), RelationGetPartitionDesc(), RelationGetRelationName, RelationGetRelid, relname, set_attnotnull(), ShareUpdateExclusiveLock, stmt, StoreCatalogInheritance(), StorePartitionBound(), StorePartitionKey(), strlcpy(), table_close(), table_open(), transformPartitionBound(), transformPartitionSpec(), transformRelOptions(), RelationData::trigdesc, typenameTypeId(), and view_reloptions().

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

◆ deleteSplitPartitionContext()

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

Definition at line 22966 of file tablecmds.c.

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

References ExecDropSingleTupleTableSlot(), fb(), FreeBulkInsertState(), lfirst, list_delete_cell(), pfree(), RelationGetRelid, AlteredTableInfo::relid, and table_finish_bulk_insert().

Referenced by SplitPartitionMoveRows().

◆ DetachPartitionFinalize()

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

Definition at line 21127 of file tablecmds.c.

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

References AccessExclusiveLock, addFkRecurseReferenced(), Assert, ATExecDropIdentity(), CacheInvalidateRelcache(), CacheInvalidateRelcacheByRelid(), CatalogTupleUpdate(), CommandCounterIncrement(), ConstraintSetParentConstraint(), copyObject, DeconstructFkConstraintRow(), deleteDependencyRecordsForClass(), DEPENDENCY_INTERNAL, DROP_RESTRICT, DropClonedTriggersFromPartition(), elog, ERROR, fb(), find_all_inheritors(), foreach_node, Form_pg_constraint, get_partition_parent(), get_relation_idx_constraint_oid(), GetForeignKeyCheckTriggers(), GetParentedForeignKeyRefs(), GETSTRUCT(), has_superclass(), heap_freetuple(), heap_modify_tuple(), HeapTupleIsValid, i, idx(), index_close(), INDEX_MAX_KEYS, index_open(), IndexGetRelation(), IndexSetParentIndex(), InvalidOid, lappend(), lappend_oid(), lfirst, lfirst_oid, list_free_deep(), list_member_oid(), makeNode, makeString(), NameStr, NIL, NoLock, ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, performDeletion(), pstrdup(), RelationData::rd_att, RelationData::rd_rel, RelationGetDescr, RelationGetFKeyList(), RelationGetIndexList(), RelationGetNumberOfAttributes, RelationGetRelid, ReleaseSysCache(), RemoveInheritance(), RowExclusiveLock, SearchSysCache1(), SearchSysCacheCopy1, ShareRowExclusiveLock, HeapTupleData::t_self, table_close(), table_open(), TriggerSetParentTrigger(), TupleDescAttr(), and update_default_partition_oid().

Referenced by ATExecDetachPartition(), ATExecDetachPartitionFinalize(), and detachPartitionTable().

◆ detachPartitionTable()

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

Definition at line 22722 of file tablecmds.c.

22723{
22724 /* Remove the pg_inherits row first. */
22726
22727 /*
22728 * Detaching the partition might involve TOAST table access, so ensure we
22729 * have a valid snapshot.
22730 */
22732
22733 /* Do the final part of detaching. */
22735
22737}

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

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ drop_parent_dependency()

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

Definition at line 18189 of file tablecmds.c.

18191{
18193 SysScanDesc scan;
18194 ScanKeyData key[3];
18196
18198
18199 ScanKeyInit(&key[0],
18203 ScanKeyInit(&key[1],
18206 ObjectIdGetDatum(relid));
18207 ScanKeyInit(&key[2],
18210 Int32GetDatum(0));
18211
18213 NULL, 3, key);
18214
18216 {
18218
18219 if (dep->refclassid == refclassid &&
18220 dep->refobjid == refobjid &&
18221 dep->refobjsubid == 0 &&
18222 dep->deptype == deptype)
18224 }
18225
18226 systable_endscan(scan);
18228}

References BTEqualStrategyNumber, CatalogTupleDelete(), fb(), Form_pg_depend, GETSTRUCT(), HeapTupleIsValid, Int32GetDatum(), ObjectIdGetDatum(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecAddOf(), ATExecDropOf(), and RemoveInheritance().

◆ DropClonedTriggersFromPartition()

static void DropClonedTriggersFromPartition ( Oid  partitionId)
static

Definition at line 21495 of file tablecmds.c.

21496{
21498 SysScanDesc scan;
21501 ObjectAddresses *objects;
21502
21503 objects = new_object_addresses();
21504
21505 /*
21506 * Scan pg_trigger to search for all triggers on this rel.
21507 */
21512 true, NULL, 1, &skey);
21514 {
21517
21518 /* Ignore triggers that weren't cloned */
21519 if (!OidIsValid(pg_trigger->tgparentid))
21520 continue;
21521
21522 /*
21523 * Ignore internal triggers that are implementation objects of foreign
21524 * keys, because these will be detached when the foreign keys
21525 * themselves are.
21526 */
21527 if (OidIsValid(pg_trigger->tgconstrrelid))
21528 continue;
21529
21530 /*
21531 * This is ugly, but necessary: remove the dependency markings on the
21532 * trigger so that it can be removed.
21533 */
21540
21541 /* remember this trigger to remove it below */
21543 add_exact_object_address(&trig, objects);
21544 }
21545
21546 /* make the dependency removal visible to the deletion below */
21549
21550 /* done */
21551 free_object_addresses(objects);
21552 systable_endscan(scan);
21554}

References add_exact_object_address(), BTEqualStrategyNumber, CommandCounterIncrement(), deleteDependencyRecordsForClass(), DEPENDENCY_PARTITION_PRI, DEPENDENCY_PARTITION_SEC, DROP_RESTRICT, fb(), Form_pg_trigger, free_object_addresses(), GETSTRUCT(), HeapTupleIsValid, new_object_addresses(), ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, PERFORM_DELETION_INTERNAL, performMultipleDeletions(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by DetachPartitionFinalize().

◆ dropconstraint_internal()

static ObjectAddress dropconstraint_internal ( Relation  rel,
HeapTuple  constraintTup,
DropBehavior  behavior,
bool  recurse,
bool  recursing,
bool  missing_ok,
LOCKMODE  lockmode 
)
static

Definition at line 14104 of file tablecmds.c.

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

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

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

◆ DropErrorMsgNonExistent()

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

Definition at line 1469 of file tablecmds.c.

1470{
1471 const struct dropmsgstrings *rentry;
1472
1473 if (rel->schemaname != NULL &&
1475 {
1476 if (!missing_ok)
1477 {
1478 ereport(ERROR,
1480 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1481 }
1482 else
1483 {
1485 (errmsg("schema \"%s\" does not exist, skipping",
1486 rel->schemaname)));
1487 }
1488 return;
1489 }
1490
1491 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1492 {
1493 if (rentry->kind == rightkind)
1494 {
1495 if (!missing_ok)
1496 {
1497 ereport(ERROR,
1498 (errcode(rentry->nonexistent_code),
1499 errmsg(rentry->nonexistent_msg, rel->relname)));
1500 }
1501 else
1502 {
1503 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1504 break;
1505 }
1506 }
1507 }
1508
1509 Assert(rentry->kind != '\0'); /* Should be impossible */
1510}
Oid LookupNamespaceNoError(const char *nspname)
Definition namespace.c:3427
char * schemaname
Definition primnodes.h:81
static const struct dropmsgstrings dropmsgstringarray[]
Definition tablecmds.c:256

References Assert, dropmsgstringarray, ereport, errcode(), errmsg(), ERROR, fb(), dropmsgstrings::kind, LookupNamespaceNoError(), NOTICE, OidIsValid, RangeVar::relname, and RangeVar::schemaname.

Referenced by RemoveRelations().

◆ DropErrorMsgWrongType()

static void DropErrorMsgWrongType ( const char relname,
char  wrongkind,
char  rightkind 
)
static

Definition at line 1517 of file tablecmds.c.

1518{
1519 const struct dropmsgstrings *rentry;
1520 const struct dropmsgstrings *wentry;
1521
1522 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1523 if (rentry->kind == rightkind)
1524 break;
1525 Assert(rentry->kind != '\0');
1526
1527 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1528 if (wentry->kind == wrongkind)
1529 break;
1530 /* wrongkind could be something we don't have in our table... */
1531
1532 ereport(ERROR,
1534 errmsg(rentry->nota_msg, relname),
1535 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1536}

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

Referenced by RangeVarCallbackForDropRelation().

◆ DropForeignKeyConstraintTriggers()

static void DropForeignKeyConstraintTriggers ( Relation  trigrel,
Oid  conoid,
Oid  confrelid,
Oid  conrelid 
)
static

Definition at line 12030 of file tablecmds.c.

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

References Assert, BTEqualStrategyNumber, CommandCounterIncrement(), deleteDependencyRecordsFor(), DROP_RESTRICT, fb(), Form_pg_trigger, GETSTRUCT(), ObjectAddressSet, ObjectIdGetDatum(), OidIsValid, performDeletion(), ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by ATExecAlterConstrEnforceability(), and AttachPartitionForeignKey().

◆ evaluateGeneratedExpressionsAndCheckConstraints()

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

Definition at line 22150 of file tablecmds.c.

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

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

Referenced by MergePartitionsMoveRows(), and SplitPartitionMoveRows().

◆ ExecuteTruncate()

void ExecuteTruncate ( TruncateStmt stmt)

Definition at line 1867 of file tablecmds.c.

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

References AccessExclusiveLock, ereport, errcode(), errhint(), errmsg(), ERROR, ExecuteTruncateGuts(), fb(), 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 1991 of file tablecmds.c.

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

References AccessExclusiveLock, aclcheck_error(), ACLCHECK_NOT_OWNER, AfterTriggerBeginQuery(), AfterTriggerEndQuery(), Assert, CheckTableForSerializableConflictIn(), CreateExecutorState(), CurrentMemoryContext, DROP_CASCADE, DROP_RESTRICT, HASHCTL::entrysize, ereport, errmsg(), EState::es_opened_result_relations, ExecASTruncateTriggers(), ExecBSTruncateTriggers(), FdwRoutine::ExecForeignTruncate, fb(), 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, 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(), ResetSequence(), RestoreUserContext(), ResultRelInfo::ri_RelationDesc, 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 6975 of file tablecmds.c.

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

References AccessShareLock, BTEqualStrategyNumber, check_stack_depth(), ereport, errcode(), errmsg(), ERROR, fb(), find_composite_type_dependencies(), Form_pg_depend, GETSTRUCT(), HeapTupleIsValid, NameStr, ObjectIdGetDatum(), OidIsValid, RelationData::rd_rel, relation_close(), relation_open(), RelationGetDescr, RelationGetRelationName, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_open(), and TupleDescAttr().

Referenced by ATPrepAlterColumnType(), ATRewriteTables(), find_composite_type_dependencies(), and get_rels_with_domain().

◆ find_typed_table_dependencies()

static List * find_typed_table_dependencies ( Oid  typeOid,
const char typeName,
DropBehavior  behavior 
)
static

Definition at line 7133 of file tablecmds.c.

7134{
7136 ScanKeyData key[1];
7137 TableScanDesc scan;
7138 HeapTuple tuple;
7139 List *result = NIL;
7140
7142
7143 ScanKeyInit(&key[0],
7146 ObjectIdGetDatum(typeOid));
7147
7148 scan = table_beginscan_catalog(classRel, 1, key);
7149
7150 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7151 {
7153
7154 if (behavior == DROP_RESTRICT)
7155 ereport(ERROR,
7157 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7158 typeName),
7159 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7160 else
7161 result = lappend_oid(result, classform->oid);
7162 }
7163
7164 table_endscan(scan);
7166
7167 return result;
7168}

References AccessShareLock, BTEqualStrategyNumber, DROP_RESTRICT, ereport, errcode(), errhint(), errmsg(), ERROR, fb(), ForwardScanDirection, GETSTRUCT(), heap_getnext(), 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 3618 of file tablecmds.c.

3619{
3620 ListCell *lc;
3621 int i = 1;
3622
3623 foreach(lc, columns)
3624 {
3625 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3626 return i;
3627
3628 i++;
3629 }
3630 return 0;
3631}

References fb(), i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 13663 of file tablecmds.c.

13664{
13665 CoercionPathType ret;
13666
13668 {
13670 *funcid = InvalidOid;
13671 }
13672 else
13673 {
13675 COERCION_IMPLICIT, funcid);
13676 if (ret == COERCION_PATH_NONE)
13677 /* A previously-relied-upon cast is now gone. */
13678 elog(ERROR, "could not find cast from %u to %u",
13680 }
13681
13682 return ret;
13683}
CoercionPathType find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, Oid *funcid)
@ COERCION_PATH_NONE
@ COERCION_PATH_RELABELTYPE

References COERCION_IMPLICIT, COERCION_PATH_NONE, COERCION_PATH_RELABELTYPE, elog, ERROR, fb(), find_coercion_pathway(), and InvalidOid.

Referenced by ATAddForeignKeyConstraint().

◆ GetAttributeCompression()

static char GetAttributeCompression ( Oid  atttypid,
const char compression 
)
static

Definition at line 22034 of file tablecmds.c.

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

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

Referenced by ATExecSetCompression(), and BuildDescForRelation().

◆ getAttributesList()

static List * getAttributesList ( Relation  parent_rel)
static

Definition at line 22195 of file tablecmds.c.

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

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

Referenced by createPartitionTable().

◆ GetAttributeStorage()

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

Definition at line 22072 of file tablecmds.c.

22073{
22074 char cstorage = 0;
22075
22076 if (pg_strcasecmp(storagemode, "plain") == 0)
22078 else if (pg_strcasecmp(storagemode, "external") == 0)
22080 else if (pg_strcasecmp(storagemode, "extended") == 0)
22082 else if (pg_strcasecmp(storagemode, "main") == 0)
22084 else if (pg_strcasecmp(storagemode, "default") == 0)
22086 else
22087 ereport(ERROR,
22089 errmsg("invalid storage type \"%s\"",
22090 storagemode)));
22091
22092 /*
22093 * safety check: do not allow toasted storage modes unless column datatype
22094 * is TOAST-aware.
22095 */
22097 ereport(ERROR,
22099 errmsg("column data type %s can only have storage PLAIN",
22101
22102 return cstorage;
22103}
char get_typstorage(Oid typid)
Definition lsyscache.c:2571
int pg_strcasecmp(const char *s1, const char *s2)

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

12102{
12104 SysScanDesc scan;
12106
12108 ScanKeyInit(&key,
12111 ObjectIdGetDatum(conoid));
12112
12114 NULL, 1, &key);
12115 while ((trigtup = systable_getnext(scan)) != NULL)
12116 {
12118
12119 if (trgform->tgconstrrelid != conrelid)
12120 continue;
12121 if (trgform->tgrelid != confrelid)
12122 continue;
12123 /* Only ever look at "action" triggers on the PK side. */
12125 continue;
12126 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12127 {
12129 *deleteTriggerOid = trgform->oid;
12130 }
12131 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12132 {
12134 *updateTriggerOid = trgform->oid;
12135 }
12136#ifndef USE_ASSERT_CHECKING
12137 /* In an assert-enabled build, continue looking to find duplicates */
12139 break;
12140#endif
12141 }
12142
12144 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12145 conoid);
12147 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12148 conoid);
12149
12150 systable_endscan(scan);
12151}
int RI_FKey_trigger_type(Oid tgfoid)
#define RI_TRIGGER_PK
Definition trigger.h:286

References Assert, BTEqualStrategyNumber, elog, ERROR, fb(), Form_pg_trigger, GETSTRUCT(), InvalidOid, 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 12159 of file tablecmds.c.

12163{
12165 SysScanDesc scan;
12167
12169 ScanKeyInit(&key,
12172 ObjectIdGetDatum(conoid));
12173
12175 NULL, 1, &key);
12176 while ((trigtup = systable_getnext(scan)) != NULL)
12177 {
12179
12180 if (trgform->tgconstrrelid != confrelid)
12181 continue;
12182 if (trgform->tgrelid != conrelid)
12183 continue;
12184 /* Only ever look at "check" triggers on the FK side. */
12186 continue;
12187 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12188 {
12190 *insertTriggerOid = trgform->oid;
12191 }
12192 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12193 {
12195 *updateTriggerOid = trgform->oid;
12196 }
12197#ifndef USE_ASSERT_CHECKING
12198 /* In an assert-enabled build, continue looking to find duplicates. */
12200 break;
12201#endif
12202 }
12203
12205 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12206 conoid);
12208 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12209 conoid);
12210
12211 systable_endscan(scan);
12212}
#define RI_TRIGGER_FK
Definition trigger.h:287

References Assert, BTEqualStrategyNumber, elog, ERROR, fb(), Form_pg_trigger, GETSTRUCT(), InvalidOid, ObjectIdGetDatum(), OidIsValid, RI_FKey_trigger_type(), RI_TRIGGER_FK, ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by AttachPartitionForeignKey(), CloneFkReferencing(), and DetachPartitionFinalize().

◆ GetParentedForeignKeyRefs()

static List * GetParentedForeignKeyRefs ( Relation  partition)
static

Definition at line 21932 of file tablecmds.c.

21933{
21935 HeapTuple tuple;
21936 SysScanDesc scan;
21937 ScanKeyData key[2];
21938 List *constraints = NIL;
21939
21940 /*
21941 * If no indexes, or no columns are referenceable by FKs, we can avoid the
21942 * scan.
21943 */
21947 return NIL;
21948
21949 /* Search for constraints referencing this table */
21951 ScanKeyInit(&key[0],
21954 ScanKeyInit(&key[1],
21957
21958 /* XXX This is a seqscan, as we don't have a usable index */
21959 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
21960 while ((tuple = systable_getnext(scan)) != NULL)
21961 {
21963
21964 /*
21965 * We only need to process constraints that are part of larger ones.
21966 */
21967 if (!OidIsValid(constrForm->conparentid))
21968 continue;
21969
21970 constraints = lappend_oid(constraints, constrForm->oid);
21971 }
21972
21973 systable_endscan(scan);
21975
21976 return constraints;
21977}
#define bms_is_empty(a)
Definition bitmapset.h:118
@ INDEX_ATTR_BITMAP_KEY
Definition relcache.h:69

References AccessShareLock, bms_is_empty, BTEqualStrategyNumber, CharGetDatum(), fb(), Form_pg_constraint, GETSTRUCT(), INDEX_ATTR_BITMAP_KEY, InvalidOid, 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 17174 of file tablecmds.c.

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

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

17893{
17895 SysScanDesc scan;
17898 bool found = false;
17899
17900 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
17901
17902 /*
17903 * Find pg_inherits entries by inhparent. (We need to scan them all in
17904 * order to verify that no other partition is pending detach.)
17905 */
17907 ScanKeyInit(&key,
17912 true, NULL, 1, &key);
17913
17915 {
17917
17919 if (inhForm->inhdetachpending)
17920 ereport(ERROR,
17922 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
17923 get_rel_name(inhForm->inhrelid),
17924 get_namespace_name(parent_rel->rd_rel->relnamespace),
17926 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
17927
17928 if (inhForm->inhrelid == RelationGetRelid(child_rel))
17929 {
17931
17934
17936 &inheritsTuple->t_self,
17937 newtup);
17938 found = true;
17940 /* keep looking, to ensure we catch others pending detach */
17941 }
17942 }
17943
17944 /* Done */
17945 systable_endscan(scan);
17947
17948 if (!found)
17949 ereport(ERROR,
17951 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
17954}
#define ERRCODE_UNDEFINED_TABLE
Definition pgbench.c:79

References Assert, BTEqualStrategyNumber, CatalogTupleUpdate(), ereport, errcode(), ERRCODE_UNDEFINED_TABLE, errhint(), errmsg(), ERROR, fb(), Form_pg_inherits, get_namespace_name(), get_rel_name(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, ObjectIdGetDatum(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecDetachPartition().

◆ MergeAttributes()

static List * MergeAttributes ( List columns,
const List supers,
char  relpersistence,
bool  is_partition,
List **  supconstr,
List **  supnotnulls 
)
static

Definition at line 2555 of file tablecmds.c.

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

References aclcheck_error(), ACLCHECK_NOT_OWNER, Assert, bms_add_member(), bms_is_member(), ConstrCheck::ccname, TupleConstr::check, CheckTableNotInUse(), ColumnDef::colname, CompressionMethodIsValid, CONSTR_NOTNULL, ColumnDef::cooked_default, elog, equal(), ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, fb(), findAttrByName(), forboth, foreach_ptr, free_attrmap(), ColumnDef::generated, get_relkind_objtype(), GetCompressionMethodName(), GetUserId(), i, InvalidOid, 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, NIL, NoLock, TupleConstr::num_check, object_ownercheck(), pstrdup(), ColumnDef::raw_default, RelationData::rd_rel, RELATION_IS_OTHER_TEMP, RelationGetDescr, RelationGetNotNullConstraints(), RelationGetRelationName, RelationGetRelid, stringToNode(), table_close(), table_open(), TupleDescAttr(), and TupleDescGetDefault().

Referenced by DefineRelation().

◆ MergeAttributesIntoExisting()

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

Definition at line 17525 of file tablecmds.c.

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

References Assert, CatalogTupleUpdate(), ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), findNotNullConstraintAttnum(), Form_pg_constraint, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, NameStr, pg_add_s16_overflow(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), table_open(), and TupleDescAttr().

Referenced by CreateInheritance().

◆ MergeCheckConstraint()

static List * MergeCheckConstraint ( List constraints,
const char name,
Node expr,
bool  is_enforced 
)
static

Definition at line 3175 of file tablecmds.c.

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

References Assert, CONSTR_CHECK, equal(), ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errmsg(), ERROR, fb(), lappend(), lfirst, name, palloc0_object, pg_add_s16_overflow(), 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 3254 of file tablecmds.c.

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

References ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, fb(), format_type_with_typemod(), get_collation_name(), GetColumnDefCollation(), list_nth_node, NOTICE, storage_name(), and typenameTypeIdAndMod().

Referenced by MergeAttributes().

◆ MergeConstraintsIntoExisting()

static void MergeConstraintsIntoExisting ( Relation  child_rel,
Relation  parent_rel 
)
static

Definition at line 17663 of file tablecmds.c.

17664{
17669 Oid parent_relid = RelationGetRelid(parent_rel);
17670 AttrMap *attmap;
17671
17673
17674 /* Outer loop scans through the parent's constraint definitions */
17678 ObjectIdGetDatum(parent_relid));
17680 true, NULL, 1, &parent_key);
17681
17684 true);
17685
17687 {
17693 bool found = false;
17694
17695 if (parent_con->contype != CONSTRAINT_CHECK &&
17696 parent_con->contype != CONSTRAINT_NOTNULL)
17697 continue;
17698
17699 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17700 if (parent_con->connoinherit)
17701 continue;
17702
17703 if (parent_con->contype == CONSTRAINT_NOTNULL)
17705 else
17707
17708 /* Search for a child constraint matching this one */
17714 true, NULL, 1, &child_key);
17715
17717 {
17720
17721 if (child_con->contype != parent_con->contype)
17722 continue;
17723
17724 /*
17725 * CHECK constraint are matched by constraint name, NOT NULL ones
17726 * by attribute number.
17727 */
17728 if (child_con->contype == CONSTRAINT_CHECK)
17729 {
17730 if (strcmp(NameStr(parent_con->conname),
17731 NameStr(child_con->conname)) != 0)
17732 continue;
17733 }
17734 else if (child_con->contype == CONSTRAINT_NOTNULL)
17735 {
17739
17742 if (parent_attno != attmap->attnums[child_attno - 1])
17743 continue;
17744
17746 /* there shouldn't be constraints on dropped columns */
17747 if (parent_attr->attisdropped || child_attr->attisdropped)
17748 elog(ERROR, "found not-null constraint on dropped columns");
17749 }
17750
17751 if (child_con->contype == CONSTRAINT_CHECK &&
17753 ereport(ERROR,
17755 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17757
17758 /*
17759 * If the child constraint is "no inherit" then cannot merge
17760 */
17761 if (child_con->connoinherit)
17762 ereport(ERROR,
17764 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17766
17767 /*
17768 * If the child constraint is "not valid" then cannot merge with a
17769 * valid parent constraint
17770 */
17771 if (parent_con->convalidated && child_con->conenforced &&
17772 !child_con->convalidated)
17773 ereport(ERROR,
17775 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17777
17778 /*
17779 * A NOT ENFORCED child constraint cannot be merged with an
17780 * ENFORCED parent constraint. However, the reverse is allowed,
17781 * where the child constraint is ENFORCED.
17782 */
17783 if (parent_con->conenforced && !child_con->conenforced)
17784 ereport(ERROR,
17786 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
17788
17789 /*
17790 * OK, bump the child constraint's inheritance count. (If we fail
17791 * later on, this change will just roll back.)
17792 */
17795
17796 if (pg_add_s16_overflow(child_con->coninhcount, 1,
17797 &child_con->coninhcount))
17798 ereport(ERROR,
17800 errmsg("too many inheritance parents"));
17801
17802 /*
17803 * In case of partitions, an inherited constraint must be
17804 * inherited only once since it cannot have multiple parents and
17805 * it is never considered local.
17806 */
17807 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17808 {
17809 Assert(child_con->coninhcount == 1);
17810 child_con->conislocal = false;
17811 }
17812
17815
17816 found = true;
17817 break;
17818 }
17819
17821
17822 if (!found)
17823 {
17824 if (parent_con->contype == CONSTRAINT_NOTNULL)
17825 ereport(ERROR,
17827 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17828 get_attname(parent_relid,
17830 false),
17832
17833 ereport(ERROR,
17835 errmsg("child table is missing constraint \"%s\"",
17836 NameStr(parent_con->conname))));
17837 }
17838 }
17839
17842}
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)

References Assert, BTEqualStrategyNumber, build_attrmap_by_name(), CatalogTupleUpdate(), constraints_equivalent(), elog, ereport, errcode(), errmsg(), ERROR, extractNotNullColumn(), fb(), Form_pg_constraint, get_attname(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvalidAttrNumber, NameStr, ObjectIdGetDatum(), pg_add_s16_overflow(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and TupleDescAttr().

Referenced by CreateInheritance().

◆ MergeInheritedAttribute()

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

Definition at line 3424 of file tablecmds.c.

3427{
3428 char *attributeName = newdef->colname;
3431 newtypeid;
3433 newtypmod;
3435 newcollid;
3436
3438 (errmsg("merging multiple inherited definitions of column \"%s\"",
3439 attributeName)));
3441
3442 /*
3443 * Must have the same type and typmod
3444 */
3448 ereport(ERROR,
3450 errmsg("inherited column \"%s\" has a type conflict",
3452 errdetail("%s versus %s",
3455
3456 /*
3457 * Must have the same collation
3458 */
3461 if (prevcollid != newcollid)
3462 ereport(ERROR,
3464 errmsg("inherited column \"%s\" has a collation conflict",
3466 errdetail("\"%s\" versus \"%s\"",
3469
3470 /*
3471 * Copy/check storage parameter
3472 */
3473 if (prevdef->storage == 0)
3474 prevdef->storage = newdef->storage;
3475 else if (prevdef->storage != newdef->storage)
3476 ereport(ERROR,
3478 errmsg("inherited column \"%s\" has a storage parameter conflict",
3480 errdetail("%s versus %s",
3481 storage_name(prevdef->storage),
3482 storage_name(newdef->storage))));
3483
3484 /*
3485 * Copy/check compression parameter
3486 */
3487 if (prevdef->compression == NULL)
3488 prevdef->compression = newdef->compression;
3489 else if (newdef->compression != NULL)
3490 {
3491 if (strcmp(prevdef->compression, newdef->compression) != 0)
3492 ereport(ERROR,
3494 errmsg("column \"%s\" has a compression method conflict",
3496 errdetail("%s versus %s",
3497 prevdef->compression, newdef->compression)));
3498 }
3499
3500 /*
3501 * Check for GENERATED conflicts
3502 */
3503 if (prevdef->generated != newdef->generated)
3504 ereport(ERROR,
3506 errmsg("inherited column \"%s\" has a generation conflict",
3507 attributeName)));
3508
3509 /*
3510 * Default and other constraints are handled by the caller.
3511 */
3512
3513 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3514 &prevdef->inhcount))
3515 ereport(ERROR,
3517 errmsg("too many inheritance parents"));
3518
3519 return prevdef;
3520}

References ColumnDef::colname, ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), format_type_with_typemod(), get_collation_name(), GetColumnDefCollation(), list_nth_node, NOTICE, pg_add_s16_overflow(), storage_name(), and typenameTypeIdAndMod().

Referenced by MergeAttributes().

◆ MergePartitionsMoveRows()

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

Definition at line 22568 of file tablecmds.c.

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

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

Referenced by ATExecMergePartitions().

◆ NotNullImpliedByRelConstraints()

static bool NotNullImpliedByRelConstraints ( Relation  rel,
Form_pg_attribute  attr 
)
static

Definition at line 8128 of file tablecmds.c.

8129{
8131
8132 nnulltest->arg = (Expr *) makeVar(1,
8133 attr->attnum,
8134 attr->atttypid,
8135 attr->atttypmod,
8136 attr->attcollation,
8137 0);
8138 nnulltest->nulltesttype = IS_NOT_NULL;
8139
8140 /*
8141 * argisrow = false is correct even for a composite column, because
8142 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8143 * case, just IS DISTINCT FROM NULL.
8144 */
8145 nnulltest->argisrow = false;
8146 nnulltest->location = -1;
8147
8149 {
8151 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8152 RelationGetRelationName(rel), NameStr(attr->attname))));
8153 return true;
8154 }
8155
8156 return false;
8157}
@ IS_NOT_NULL
Definition primnodes.h:1978
ParseLoc location
Definition primnodes.h:311
static bool ConstraintImpliedByRelConstraint(Relation scanrel, List *testConstraint, List *provenConstraint)

References ConstraintImpliedByRelConstraint(), DEBUG1, ereport, errmsg_internal(), fb(), IS_NOT_NULL, list_make1, Var::location, makeNode, makeVar(), NameStr, NIL, and RelationGetRelationName.

Referenced by set_attnotnull().

◆ PartConstraintImpliedByRelConstraint()

bool PartConstraintImpliedByRelConstraint ( Relation  scanrel,
List partConstraint 
)

Definition at line 20084 of file tablecmds.c.

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

References ATTNULLABLE_VALID, ConstraintImpliedByRelConstraint(), fb(), TupleConstr::has_not_null, i, IS_NOT_NULL, lappend(), Var::location, makeNode, makeVar(), TupleDescData::natts, NIL, RelationData::rd_att, RelationGetDescr, TupleDescAttr(), and TupleDescCompactAttr().

Referenced by check_default_partition_contents(), and QueuePartitionConstraintValidation().

◆ PreCommit_on_commit_actions()

void PreCommit_on_commit_actions ( void  )

Definition at line 19345 of file tablecmds.c.

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

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

Referenced by CommitTransaction(), and PrepareTransaction().

◆ QueueCheckConstraintValidation()

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

Definition at line 13144 of file tablecmds.c.

13147{
13149 AlteredTableInfo *tab;
13152
13153 List *children = NIL;
13154 ListCell *child;
13156 Datum val;
13157 char *conbin;
13158
13160 Assert(con->contype == CONSTRAINT_CHECK);
13161
13162 /*
13163 * If we're recursing, the parent has already done this, so skip it. Also,
13164 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13165 * for it in the children.
13166 */
13167 if (!recursing && !con->connoinherit)
13168 children = find_all_inheritors(RelationGetRelid(rel),
13169 lockmode, NULL);
13170
13171 /*
13172 * For CHECK constraints, we must ensure that we only mark the constraint
13173 * as validated on the parent if it's already validated on the children.
13174 *
13175 * We recurse before validating on the parent, to reduce risk of
13176 * deadlocks.
13177 */
13178 foreach(child, children)
13179 {
13180 Oid childoid = lfirst_oid(child);
13182
13183 if (childoid == RelationGetRelid(rel))
13184 continue;
13185
13186 /*
13187 * If we are told not to recurse, there had better not be any child
13188 * tables, because we can't mark the constraint on the parent valid
13189 * unless it is valid for all child tables.
13190 */
13191 if (!recurse)
13192 ereport(ERROR,
13194 errmsg("constraint must be validated on child tables too")));
13195
13196 /* find_all_inheritors already got lock */
13198
13200 true, lockmode);
13202 }
13203
13204 /* Queue validation for phase 3 */
13206 newcon->name = constrName;
13207 newcon->contype = CONSTR_CHECK;
13208 newcon->refrelid = InvalidOid;
13209 newcon->refindid = InvalidOid;
13210 newcon->conid = con->oid;
13211
13216
13217 /* Find or create work queue entry for this table */
13218 tab = ATGetQueueEntry(wqueue, rel);
13219 tab->constraints = lappend(tab->constraints, newcon);
13220
13221 /*
13222 * Invalidate relcache so that others see the new validated constraint.
13223 */
13225
13226 /*
13227 * Now update the catalog, while we have the door open.
13228 */
13231 copy_con->convalidated = true;
13233
13235
13237}
long val
Definition informix.c:689
Datum SysCacheGetAttrNotNull(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition syscache.c:625

References Assert, ATExecValidateConstraint(), ATGetQueueEntry(), CacheInvalidateRelcache(), CatalogTupleUpdate(), CONSTR_CHECK, AlteredTableInfo::constraints, ereport, errcode(), errmsg(), ERROR, expand_generated_columns_in_expr(), fb(), find_all_inheritors(), Form_pg_constraint, GETSTRUCT(), heap_copytuple(), heap_freetuple(), InvalidOid, InvokeObjectPostAlterHook, lappend(), lfirst_oid, NIL, NoLock, palloc0_object, RelationGetRelid, stringToNode(), SysCacheGetAttrNotNull(), table_close(), table_open(), TextDatumGetCString, and val.

Referenced by ATExecValidateConstraint().

◆ QueueFKConstraintValidation()

static void QueueFKConstraintValidation ( List **  wqueue,
Relation  conrel,
Relation  fkrel,
Oid  pkrelid,
HeapTuple  contuple,
LOCKMODE  lockmode 
)
static

Definition at line 13025 of file tablecmds.c.

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

References Assert, ATGetQueueEntry(), BTEqualStrategyNumber, CatalogTupleUpdate(), CONSTR_FOREIGN, AlteredTableInfo::constraints, fb(), Form_pg_constraint, get_rel_relkind(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, lappend(), makeNode, NameStr, NoLock, ObjectIdGetDatum(), palloc0_object, pstrdup(), QueueFKConstraintValidation(), ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecValidateConstraint(), AttachPartitionForeignKey(), and QueueFKConstraintValidation().

◆ QueueNNConstraintValidation()

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

Definition at line 13247 of file tablecmds.c.

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

References Assert, ATExecValidateConstraint(), ATGetQueueEntry(), attnum, CacheInvalidateRelcache(), CatalogTupleUpdate(), elog, ereport, errcode(), errmsg(), ERROR, extractNotNullColumn(), fb(), find_all_inheritors(), findNotNullConstraint(), foreach_oid, Form_pg_constraint, get_attname(), get_rel_name(), GETSTRUCT(), heap_copytuple(), heap_freetuple(), InvokeObjectPostAlterHook, NameStr, NIL, NoLock, pstrdup(), RelationGetRelid, set_attnotnull(), table_close(), table_open(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATExecValidateConstraint().

◆ QueuePartitionConstraintValidation()

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

Definition at line 20202 of file tablecmds.c.

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

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

Referenced by ATExecAttachPartition(), and QueuePartitionConstraintValidation().

◆ RangeVarCallbackForAlterRelation()

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

Definition at line 19611 of file tablecmds.c.

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

References ACL_CREATE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, allowSystemTableMods, arg, elog, ereport, errcode(), errhint(), errmsg(), ERROR, fb(), get_namespace_name(), get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), HeapTupleIsValid, IsA, IsSystemClass(), nodeTag, object_aclcheck(), OBJECT_FOREIGN_TABLE, OBJECT_INDEX, OBJECT_MATVIEW, object_ownercheck(), OBJECT_SCHEMA, OBJECT_SEQUENCE, OBJECT_TABLE, OBJECT_TYPE, OBJECT_VIEW, ObjectIdGetDatum(), ReleaseSysCache(), RangeVar::relname, SearchSysCache1(), and stmt.

Referenced by AlterTableLookupRelation(), AlterTableNamespace(), and RenameRelation().

◆ RangeVarCallbackForAttachIndex()

static void RangeVarCallbackForAttachIndex ( const RangeVar rv,
Oid  relOid,
Oid  oldRelOid,
void arg 
)
static

Definition at line 21568 of file tablecmds.c.

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

References AccessShareLock, arg, ereport, errcode(), errmsg(), ERROR, fb(), 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 1708 of file tablecmds.c.

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

References AccessExclusiveLock, aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, arg, DropErrorMsgWrongType(), ereport, errcode(), errmsg(), ERROR, DropRelationCallbackState::expected_relkind, fb(), Form_pg_index, 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 3997 of file tablecmds.c.

3999{
4000 HeapTuple tuple;
4002
4003 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
4004 if (!HeapTupleIsValid(tuple))
4005 return; /* concurrently dropped */
4006 form = (Form_pg_class) GETSTRUCT(tuple);
4007 renameatt_check(relid, form, false);
4008 ReleaseSysCache(tuple);
4009}
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition tablecmds.c:3803

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

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

References elog, ERROR, fb(), 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 19519 of file tablecmds.c.

19521{
19522 char relkind;
19524
19525 /* Nothing to do if the relation was not found. */
19526 if (!OidIsValid(relId))
19527 return;
19528
19529 /*
19530 * If the relation does exist, check whether it's an index. But note that
19531 * the relation might have been dropped between the time we did the name
19532 * lookup and now. In that case, there's nothing to do.
19533 */
19534 relkind = get_rel_relkind(relId);
19535 if (!relkind)
19536 return;
19537 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19538 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19539 ereport(ERROR,
19541 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19542
19543 /* Check permissions */
19545 if (aclresult != ACLCHECK_OK)
19548 relation->relname);
19549}
#define ACL_MAINTAIN
Definition parsenodes.h:90

References ACL_MAINTAIN, aclcheck_error(), ACLCHECK_OK, ereport, errcode(), errmsg(), ERROR, fb(), 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 19579 of file tablecmds.c.

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

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

Referenced by AlterSequence(), ProcessUtilitySlow(), transformPartitionCmdForMerge(), and transformPartitionCmdForSplit().

◆ RebuildConstraintComment()

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

Definition at line 15869 of file tablecmds.c.

15872{
15873 CommentStmt *cmd;
15874 char *comment_str;
15876
15877 /* Look for comment for object wanted, and leave if none */
15879 if (comment_str == NULL)
15880 return;
15881
15882 /* Build CommentStmt node, copying all input data for safety */
15883 cmd = makeNode(CommentStmt);
15884 if (rel)
15885 {
15887 cmd->object = (Node *)
15890 makeString(pstrdup(conname)));
15891 }
15892 else
15893 {
15895 cmd->object = (Node *)
15897 makeString(pstrdup(conname)));
15898 }
15899 cmd->comment = comment_str;
15900
15901 /* Append it to list of commands */
15903 newcmd->subtype = AT_ReAddComment;
15904 newcmd->def = (Node *) cmd;
15905 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
15906}
TypeName * makeTypeNameFromNameList(List *names)
Definition makefuncs.c:531
@ OBJECT_TABCONSTRAINT
@ OBJECT_DOMCONSTRAINT
#define list_make3(x1, x2, x3)
Definition pg_list.h:216
#define list_make2(x1, x2)
Definition pg_list.h:214
char * comment
ObjectType objtype
Node * object

References AT_ReAddComment, CommentStmt::comment, copyObject, fb(), get_namespace_name(), GetComment(), lappend(), list_make2, list_make3, makeNode, makeString(), makeTypeNameFromNameList(), CommentStmt::object, OBJECT_DOMCONSTRAINT, OBJECT_TABCONSTRAINT, CommentStmt::objtype, pstrdup(), RelationGetNamespace, RelationGetRelationName, and AlteredTableInfo::subcmds.

Referenced by ATPostAlterTypeParse().

◆ refuseDupeIndexAttach()

static void refuseDupeIndexAttach ( Relation  parentIdx,
Relation  partIdx,
Relation  partitionTbl 
)
static

Definition at line 21784 of file tablecmds.c.

21785{
21787
21788 existingIdx = index_get_partition(partitionTbl,
21789 RelationGetRelid(parentIdx));
21791 ereport(ERROR,
21793 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21795 RelationGetRelationName(parentIdx)),
21796 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
21798 RelationGetRelationName(partitionTbl))));
21799}

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

Referenced by ATExecAttachPartitionIdx().

◆ register_on_commit_action()

void register_on_commit_action ( Oid  relid,
OnCommitAction  action 
)

Definition at line 19286 of file tablecmds.c.

19287{
19290
19291 /*
19292 * We needn't bother registering the relation unless there is an ON COMMIT
19293 * action we need to take.
19294 */
19295 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19296 return;
19297
19299
19301 oc->relid = relid;
19302 oc->oncommit = action;
19303 oc->creating_subid = GetCurrentSubTransactionId();
19304 oc->deleting_subid = InvalidSubTransactionId;
19305
19306 /*
19307 * We use lcons() here so that ON COMMIT actions are processed in reverse
19308 * order of registration. That might not be essential but it seems
19309 * reasonable.
19310 */
19312
19314}
List * lcons(void *datum, List *list)
Definition list.c:495
MemoryContext CacheMemoryContext
Definition mcxt.c:169

References CacheMemoryContext, fb(), GetCurrentSubTransactionId(), InvalidSubTransactionId, lcons(), MemoryContextSwitchTo(), on_commits, ONCOMMIT_NOOP, ONCOMMIT_PRESERVE_ROWS, and palloc_object.

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

18429{
18436 ListCell *index;
18437
18438 /*
18439 * Check whether relreplident has changed, and update it if so.
18440 */
18445 elog(ERROR, "cache lookup failed for relation \"%s\"",
18448 if (pg_class_form->relreplident != ri_type)
18449 {
18450 pg_class_form->relreplident = ri_type;
18452 }
18455
18456 /*
18457 * Update the per-index indisreplident flags correctly.
18458 */
18460 foreach(index, RelationGetIndexList(rel))
18461 {
18463 bool dirty = false;
18464
18468 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18470
18471 if (thisIndexOid == indexOid)
18472 {
18473 /* Set the bit if not already set. */
18474 if (!pg_index_form->indisreplident)
18475 {
18476 dirty = true;
18477 pg_index_form->indisreplident = true;
18478 }
18479 }
18480 else
18481 {
18482 /* Unset the bit if set. */
18483 if (pg_index_form->indisreplident)
18484 {
18485 dirty = true;
18486 pg_index_form->indisreplident = false;
18487 }
18488 }
18489
18490 if (dirty)
18491 {
18494 InvalidOid, is_internal);
18495
18496 /*
18497 * Invalidate the relcache for the table, so that after we commit
18498 * all sessions will refresh the table's replica identity index
18499 * before attempting any UPDATE or DELETE on the table. (If we
18500 * changed the table's pg_class row above, then a relcache inval
18501 * is already queued due to that; but we might not have.)
18502 */
18504 }
18506 }
18507
18509}
Definition type.h:96

References CacheInvalidateRelcache(), CatalogTupleUpdate(), elog, ERROR, fb(), Form_pg_index, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidOid, InvokeObjectPostAlterHookArg, lfirst_oid, ObjectIdGetDatum(), RelationGetIndexList(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopy1, 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 15070 of file tablecmds.c.

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

References Assert, AT_AlterColumnType, AT_SetExpression, attnum, BTEqualStrategyNumber, ObjectAddress::classId, elog, ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), Form_pg_depend, get_attname(), get_rel_relkind(), GetAttrDefaultColumnAddress(), getObjectDescription(), GETSTRUCT(), HeapTupleIsValid, Int32GetDatum(), NoLock, ObjectIdGetDatum(), 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 15311 of file tablecmds.c.

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

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

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

References AlteredTableInfo::changedConstraintDefs, AlteredTableInfo::changedConstraintOids, fb(), get_constraint_index(), get_constraint_type(), lappend(), lappend_oid(), lcons(), lcons_oid(), list_member_oid(), OidIsValid, pg_get_constraintdef_command(), RememberClusterOnForRebuilding(), and RememberReplicaIdentityForRebuilding().

Referenced by RememberAllDependentForRebuilding(), and RememberIndexForRebuilding().

◆ RememberIndexForRebuilding()

static void RememberIndexForRebuilding ( Oid  indoid,
AlteredTableInfo tab 
)
static

Definition at line 15383 of file tablecmds.c.

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

References AlteredTableInfo::changedIndexDefs, AlteredTableInfo::changedIndexOids, fb(), 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 15296 of file tablecmds.c.

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

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

15435{
15436 /*
15437 * This de-duplication check is critical for two independent reasons: we
15438 * mustn't try to recreate the same statistics object twice, and if the
15439 * statistics object depends on more than one column whose type is to be
15440 * altered, we must capture its definition string before applying any of
15441 * the type changes. ruleutils.c will get confused if we ask again later.
15442 */
15444 {
15445 /* OK, capture the statistics object's existing definition string */
15447
15449 stxoid);
15451 defstring);
15452 }
15453}
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition ruleutils.c:1627

References AlteredTableInfo::changedStatisticsDefs, AlteredTableInfo::changedStatisticsOids, fb(), 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 19322 of file tablecmds.c.

19323{
19324 ListCell *l;
19325
19326 foreach(l, on_commits)
19327 {
19329
19330 if (oc->relid == relid)
19331 {
19333 break;
19334 }
19335 }
19336}
SubTransactionId deleting_subid
Definition tablecmds.c:129

References OnCommitItem::deleting_subid, fb(), GetCurrentSubTransactionId(), lfirst, and on_commits.

Referenced by heap_drop_with_catalog().

◆ RemoveInheritance()

static void RemoveInheritance ( Relation  child_rel,
Relation  parent_rel,
bool  expect_detached 
)
static

Definition at line 17975 of file tablecmds.c.

17976{
17978 SysScanDesc scan;
17979 ScanKeyData key[3];
17982 AttrMap *attmap;
17983 List *connames;
17984 List *nncolumns;
17985 bool found;
17986 bool is_partitioning;
17987
17989
17994 if (!found)
17995 {
17996 if (is_partitioning)
17997 ereport(ERROR,
17999 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18002 else
18003 ereport(ERROR,
18005 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18008 }
18009
18010 /*
18011 * Search through child columns looking for ones matching parent rel
18012 */
18014 ScanKeyInit(&key[0],
18019 true, NULL, 1, key);
18021 {
18023
18024 /* Ignore if dropped or not inherited */
18025 if (att->attisdropped)
18026 continue;
18027 if (att->attinhcount <= 0)
18028 continue;
18029
18031 NameStr(att->attname)))
18032 {
18033 /* Decrement inhcount and possibly set islocal to true */
18036
18037 copy_att->attinhcount--;
18038 if (copy_att->attinhcount == 0)
18039 copy_att->attislocal = true;
18040
18043 }
18044 }
18045 systable_endscan(scan);
18047
18048 /*
18049 * Likewise, find inherited check and not-null constraints and disinherit
18050 * them. To do this, we first need a list of the names of the parent's
18051 * check constraints. (We cheat a bit by only checking for name matches,
18052 * assuming that the expressions will match.)
18053 *
18054 * For NOT NULL columns, we store column numbers to match, mapping them in
18055 * to the child rel's attribute numbers.
18056 */
18059 false);
18060
18062 ScanKeyInit(&key[0],
18067 true, NULL, 1, key);
18068
18069 connames = NIL;
18070 nncolumns = NIL;
18071
18073 {
18075
18076 if (con->connoinherit)
18077 continue;
18078
18079 if (con->contype == CONSTRAINT_CHECK)
18080 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18081 if (con->contype == CONSTRAINT_NOTNULL)
18082 {
18084
18086 }
18087 }
18088
18089 systable_endscan(scan);
18090
18091 /* Now scan the child's constraints to find matches */
18092 ScanKeyInit(&key[0],
18097 true, NULL, 1, key);
18098
18100 {
18102 bool match = false;
18103
18104 /*
18105 * Match CHECK constraints by name, not-null constraints by column
18106 * number, and ignore all others.
18107 */
18108 if (con->contype == CONSTRAINT_CHECK)
18109 {
18111 {
18112 if (con->contype == CONSTRAINT_CHECK &&
18113 strcmp(NameStr(con->conname), chkname) == 0)
18114 {
18115 match = true;
18117 break;
18118 }
18119 }
18120 }
18121 else if (con->contype == CONSTRAINT_NOTNULL)
18122 {
18124
18126 {
18127 if (prevattno == child_attno)
18128 {
18129 match = true;
18131 break;
18132 }
18133 }
18134 }
18135 else
18136 continue;
18137
18138 if (match)
18139 {
18140 /* Decrement inhcount and possibly set islocal to true */
18143
18144 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18145 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18147
18148 copy_con->coninhcount--;
18149 if (copy_con->coninhcount == 0)
18150 copy_con->conislocal = true;
18151
18154 }
18155 }
18156
18157 /* We should have matched all constraints */
18158 if (connames != NIL || nncolumns != NIL)
18159 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18162
18163 systable_endscan(scan);
18165
18170
18171 /*
18172 * Post alter hook of this inherits. Since object_access_hook doesn't take
18173 * multiple object identifiers, we relay oid of parent relation using
18174 * auxiliary_id argument.
18175 */
18179}
bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending, const char *childname)
bool SearchSysCacheExistsAttName(Oid relid, const char *attname)
Definition syscache.c:517
#define child_dependency_type(child_is_partition)
Definition tablecmds.c:366

References BTEqualStrategyNumber, build_attrmap_by_name(), CatalogTupleUpdate(), child_dependency_type, DeleteInheritsTuple(), drop_parent_dependency(), elog, ereport, errcode(), ERRCODE_UNDEFINED_TABLE, errmsg(), ERROR, extractNotNullColumn(), fb(), foreach_delete_current, foreach_int, foreach_ptr, Form_pg_constraint, GETSTRUCT(), heap_copytuple(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHookArg, lappend(), lappend_int(), list_length(), NameStr, NIL, ObjectIdGetDatum(), pstrdup(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, RowExclusiveLock, ScanKeyInit(), SearchSysCacheExistsAttName(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATExecDetachPartition(), ATExecDropInherit(), DetachPartitionFinalize(), and detachPartitionTable().

◆ RemoveInheritedConstraint()

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

Definition at line 11948 of file tablecmds.c.

11950{
11954 SysScanDesc scan;
11956
11957 ScanKeyInit(&key,
11960 ObjectIdGetDatum(conrelid));
11961
11964 true, NULL, 1, &key);
11966 while ((consttup = systable_getnext(scan)) != NULL)
11967 {
11969
11970 if (conform->conparentid != conoid)
11971 continue;
11972 else
11973 {
11974 ObjectAddress addr;
11978
11981
11982 /*
11983 * First we must delete the dependency record that binds the
11984 * constraint records together.
11985 */
11987 conform->oid,
11990 conoid);
11991 Assert(n == 1); /* actually only one is expected */
11992
11993 /*
11994 * Now search for the triggers for this constraint and set them up
11995 * for deletion too
11996 */
12002 true, NULL, 1, &key2);
12003 while ((trigtup = systable_getnext(scan2)) != NULL)
12004 {
12008 }
12010 }
12011 }
12012 /* make the dependency deletions visible */
12016 systable_endscan(scan);
12017}
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:235
long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype, Oid refclassId, Oid refobjectId)
Definition pg_depend.c:400

References add_exact_object_address(), Assert, BTEqualStrategyNumber, CommandCounterIncrement(), deleteDependencyRecordsForSpecific(), DEPENDENCY_INTERNAL, DROP_RESTRICT, fb(), Form_pg_constraint, Form_pg_trigger, GETSTRUCT(), new_object_addresses(), ObjectAddressSet, ObjectIdGetDatum(), PERFORM_DELETION_INTERNAL, performMultipleDeletions(), PG_USED_FOR_ASSERTS_ONLY, ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by AttachPartitionForeignKey().

◆ RemoveRelations()

void RemoveRelations ( DropStmt drop)

Definition at line 1544 of file tablecmds.c.

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

References AcceptInvalidationMessages(), AccessExclusiveLock, add_exact_object_address(), Assert, ObjectAddress::classId, DROP_CASCADE, DropErrorMsgNonExistent(), elog, ereport, errcode(), errmsg(), ERROR, fb(), find_all_inheritors(), free_object_addresses(), InvalidOid, lfirst, list_length(), makeRangeVarFromNameList(), new_object_addresses(), OBJECT_FOREIGN_TABLE, OBJECT_INDEX, OBJECT_MATVIEW, OBJECT_SEQUENCE, OBJECT_TABLE, OBJECT_VIEW, ObjectAddress::objectId, ObjectAddress::objectSubId, OidIsValid, PERFORM_DELETION_CONCURRENTLY, performMultipleDeletions(), RangeVarCallbackForDropRelation(), RangeVarGetRelidExtended(), RangeVar::relname, 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 4055 of file tablecmds.c.

4062{
4065 HeapTuple tuple;
4067 ObjectAddress address;
4068
4069 Assert(!myrelid || !mytypid);
4070
4071 if (mytypid)
4072 {
4074 }
4075 else
4076 {
4078
4079 /*
4080 * don't tell it whether we're recursing; we allow changing typed
4081 * tables here
4082 */
4084
4086 }
4087
4089 if (!HeapTupleIsValid(tuple))
4090 elog(ERROR, "cache lookup failed for constraint %u",
4092 con = (Form_pg_constraint) GETSTRUCT(tuple);
4093
4094 if (myrelid &&
4095 (con->contype == CONSTRAINT_CHECK ||
4096 con->contype == CONSTRAINT_NOTNULL) &&
4097 !con->connoinherit)
4098 {
4099 if (recurse)
4100 {
4103 ListCell *lo,
4104 *li;
4105
4108
4110 {
4112 int numparents = lfirst_int(li);
4113
4114 if (childrelid == myrelid)
4115 continue;
4116
4118 }
4119 }
4120 else
4121 {
4122 if (expected_parents == 0 &&
4124 ereport(ERROR,
4126 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4127 oldconname)));
4128 }
4129
4130 if (con->coninhcount > expected_parents)
4131 ereport(ERROR,
4133 errmsg("cannot rename inherited constraint \"%s\"",
4134 oldconname)));
4135 }
4136
4137 if (con->conindid
4138 && (con->contype == CONSTRAINT_PRIMARY
4139 || con->contype == CONSTRAINT_UNIQUE
4140 || con->contype == CONSTRAINT_EXCLUSION))
4141 /* rename the index; this renames the constraint as well */
4142 RenameRelationInternal(con->conindid, newconname, false, true);
4143 else
4145
4147
4148 ReleaseSysCache(tuple);
4149
4150 if (targetrelation)
4151 {
4152 /*
4153 * Invalidate relcache so as others can see the new constraint name.
4154 */
4156
4157 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4158 }
4159
4160 return address;
4161}
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:4055

References AccessExclusiveLock, Assert, CacheInvalidateRelcache(), elog, ereport, errcode(), errmsg(), ERROR, fb(), find_all_inheritors(), find_inheritance_children(), forboth, Form_pg_constraint, get_domain_constraint_oid(), get_relation_constraint_oid(), GETSTRUCT(), HeapTupleIsValid, InvalidOid, lfirst_int, lfirst_oid, NIL, NoLock, ObjectAddressSet, ObjectIdGetDatum(), relation_close(), relation_open(), RelationGetForm, ReleaseSysCache(), rename_constraint_internal(), renameatt_check(), RenameConstraintById(), RenameRelationInternal(), and SearchSysCache1().

Referenced by rename_constraint_internal(), and RenameConstraint().

◆ renameatt()

ObjectAddress renameatt ( RenameStmt stmt)

Definition at line 4017 of file tablecmds.c.

4018{
4019 Oid relid;
4021 ObjectAddress address;
4022
4023 /* lock level taken here should match renameatt_internal */
4025 stmt->missing_ok ? RVR_MISSING_OK : 0,
4027 NULL);
4028
4029 if (!OidIsValid(relid))
4030 {
4032 (errmsg("relation \"%s\" does not exist, skipping",
4033 stmt->relation->relname)));
4034 return InvalidObjectAddress;
4035 }
4036
4037 attnum =
4038 renameatt_internal(relid,
4039 stmt->subname, /* old att name */
4040 stmt->newname, /* new att name */
4041 stmt->relation->inh, /* recursive? */
4042 false, /* recursing? */
4043 0, /* expected inhcount */
4044 stmt->behavior);
4045
4047
4048 return address;
4049}
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition tablecmds.c:3852
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition tablecmds.c:3997

References AccessExclusiveLock, attnum, ereport, errmsg(), fb(), 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 3803 of file tablecmds.c.

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

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, ereport, errcode(), errdetail_relkind_not_supported(), errmsg(), ERROR, fb(), 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 3852 of file tablecmds.c.

3859{
3865
3866 /*
3867 * Grab an exclusive lock on the target table, which we will NOT release
3868 * until end of transaction.
3869 */
3872
3873 /*
3874 * if the 'recurse' flag is set then we are supposed to rename this
3875 * attribute in all classes that inherit from 'relname' (as well as in
3876 * 'relname').
3877 *
3878 * any permissions or problems with duplicate attributes will cause the
3879 * whole transaction to abort, which is what we want -- all or nothing.
3880 */
3881 if (recurse)
3882 {
3885 ListCell *lo,
3886 *li;
3887
3888 /*
3889 * we need the number of parents for each child so that the recursive
3890 * calls to renameatt() can determine whether there are any parents
3891 * outside the inheritance hierarchy being processed.
3892 */
3895
3896 /*
3897 * find_all_inheritors does the recursive search of the inheritance
3898 * hierarchy, so all we have to do is process all of the relids in the
3899 * list that it returns.
3900 */
3902 {
3904 int numparents = lfirst_int(li);
3905
3906 if (childrelid == myrelid)
3907 continue;
3908 /* note we need not recurse again */
3910 }
3911 }
3912 else
3913 {
3914 /*
3915 * If we are told not to recurse, there had better not be any child
3916 * tables; else the rename would put them out of step.
3917 *
3918 * expected_parents will only be 0 if we are not already recursing.
3919 */
3920 if (expected_parents == 0 &&
3922 ereport(ERROR,
3924 errmsg("inherited column \"%s\" must be renamed in child tables too",
3925 oldattname)));
3926 }
3927
3928 /* rename attributes in typed tables of composite type */
3929 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3930 {
3932 ListCell *lo;
3933
3936 behavior);
3937
3938 foreach(lo, child_oids)
3939 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3940 }
3941
3943
3946 ereport(ERROR,
3948 errmsg("column \"%s\" does not exist",
3949 oldattname)));
3951
3952 attnum = attform->attnum;
3953 if (attnum <= 0)
3954 ereport(ERROR,
3956 errmsg("cannot rename system column \"%s\"",
3957 oldattname)));
3958
3959 /*
3960 * if the attribute is inherited, forbid the renaming. if this is a
3961 * top-level call to renameatt(), then expected_parents will be 0, so the
3962 * effect of this code will be to prohibit the renaming if the attribute
3963 * is inherited at all. if this is a recursive call to renameatt(),
3964 * expected_parents will be the number of parents the current relation has
3965 * within the inheritance hierarchy being processed, so we'll prohibit the
3966 * renaming only if there are additional parents from elsewhere.
3967 */
3968 if (attform->attinhcount > expected_parents)
3969 ereport(ERROR,
3971 errmsg("cannot rename inherited column \"%s\"",
3972 oldattname)));
3973
3974 /* new name should not already exist */
3976
3977 /* apply the update */
3978 namestrcpy(&(attform->attname), newattname);
3979
3981
3983
3985
3987
3988 relation_close(targetrelation, NoLock); /* close rel but keep lock */
3989
3990 return attnum;
3991}
void namestrcpy(Name name, const char *str)
Definition name.c:233

References AccessExclusiveLock, attnum, CatalogTupleUpdate(), check_for_column_name_collision(), ereport, errcode(), errmsg(), ERROR, fb(), find_all_inheritors(), find_inheritance_children(), find_typed_table_dependencies(), forboth, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, lfirst_int, lfirst_oid, namestrcpy(), NIL, NoLock, relation_close(), relation_open(), RelationGetForm, RelationGetRelationName, renameatt_check(), renameatt_internal(), RowExclusiveLock, SearchSysCacheCopyAttName(), table_close(), and table_open().

Referenced by renameatt(), and renameatt_internal().

◆ RenameConstraint()

ObjectAddress RenameConstraint ( RenameStmt stmt)

Definition at line 4164 of file tablecmds.c.

4165{
4166 Oid relid = InvalidOid;
4167 Oid typid = InvalidOid;
4168
4169 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4170 {
4171 Relation rel;
4172 HeapTuple tup;
4173
4177 if (!HeapTupleIsValid(tup))
4178 elog(ERROR, "cache lookup failed for type %u", typid);
4181 table_close(rel, NoLock);
4182 }
4183 else
4184 {
4185 /* lock level taken here should match rename_constraint_internal */
4187 stmt->missing_ok ? RVR_MISSING_OK : 0,
4189 NULL);
4190 if (!OidIsValid(relid))
4191 {
4193 (errmsg("relation \"%s\" does not exist, skipping",
4194 stmt->relation->relname)));
4195 return InvalidObjectAddress;
4196 }
4197 }
4198
4199 return
4200 rename_constraint_internal(relid, typid,
4201 stmt->subname,
4202 stmt->newname,
4203 (stmt->relation &&
4204 stmt->relation->inh), /* recursive? */
4205 false, /* recursing? */
4206 0 /* expected inhcount */ );
4207}
void checkDomainOwner(HeapTuple tup)
Definition typecmds.c:3527

References AccessExclusiveLock, castNode, checkDomainOwner(), elog, ereport, errmsg(), ERROR, fb(), 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 4214 of file tablecmds.c.

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

References AccessExclusiveLock, ereport, errmsg(), fb(), 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 4278 of file tablecmds.c.

4279{
4281 Relation relrelation; /* for RELATION relation */
4286
4287 /*
4288 * Grab a lock on the target relation, which we will NOT release until end
4289 * of transaction. We need at least a self-exclusive lock so that
4290 * concurrent DDL doesn't overwrite the rename if they start updating
4291 * while still seeing the old version. The lock also guards against
4292 * triggering relcache reloads in concurrent sessions, which might not
4293 * handle this information changing under them. For indexes, we can use a
4294 * reduced lock level because RelationReloadIndexInfo() handles indexes
4295 * specially.
4296 */
4299
4300 /*
4301 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4302 */
4304
4306 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4307 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4308 otid = reltup->t_self;
4310
4312 ereport(ERROR,
4314 errmsg("relation \"%s\" already exists",
4315 newrelname)));
4316
4317 /*
4318 * RenameRelation is careful not to believe the caller's idea of the
4319 * relation kind being handled. We don't have to worry about this, but
4320 * let's not be totally oblivious to it. We can process an index as
4321 * not-an-index, but not the other way around.
4322 */
4323 Assert(!is_index ||
4324 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4325 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4326
4327 /*
4328 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4329 * because it's a copy...)
4330 */
4331 namestrcpy(&(relform->relname), newrelname);
4332
4335
4337 InvalidOid, is_internal);
4338
4341
4342 /*
4343 * Also rename the associated type, if any.
4344 */
4345 if (OidIsValid(targetrelation->rd_rel->reltype))
4346 RenameTypeInternal(targetrelation->rd_rel->reltype,
4348
4349 /*
4350 * Also rename the associated constraint, if any.
4351 */
4352 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4353 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4354 {
4356
4359 }
4360
4361 /*
4362 * Close rel, but keep lock!
4363 */
4365}
void RenameTypeInternal(Oid typeOid, const char *newTypeName, Oid typeNamespace)
Definition pg_type.c:763

References AccessExclusiveLock, Assert, CatalogTupleUpdate(), elog, ereport, errcode(), errmsg(), ERROR, fb(), get_index_constraint(), get_relname_relid(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InplaceUpdateTupleLock, InvalidOid, InvokeObjectPostAlterHookArg, namestrcpy(), NoLock, ObjectIdGetDatum(), OidIsValid, relation_close(), relation_open(), RelationGetNamespace, RenameConstraintById(), RenameTypeInternal(), RowExclusiveLock, SearchSysCacheLockedCopy1(), ShareUpdateExclusiveLock, table_close(), table_open(), and UnlockTuple().

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

◆ ResetRelRewrite()

void ResetRelRewrite ( Oid  myrelid)

Definition at line 4371 of file tablecmds.c.

4372{
4373 Relation relrelation; /* for RELATION relation */
4376
4377 /*
4378 * Find relation's pg_class tuple.
4379 */
4381
4383 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4384 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4386
4387 /*
4388 * Update pg_class tuple.
4389 */
4390 relform->relrewrite = InvalidOid;
4391
4393
4396}

References CatalogTupleUpdate(), elog, ERROR, fb(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidOid, ObjectIdGetDatum(), RowExclusiveLock, SearchSysCacheCopy1, table_close(), and table_open().

Referenced by finish_heap_swap().

◆ set_attnotnull()

static void set_attnotnull ( List **  wqueue,
Relation  rel,
AttrNumber  attnum,
bool  is_valid,
bool  queue_validation 
)
static

Definition at line 7879 of file tablecmds.c.

7881{
7882 Form_pg_attribute attr;
7884
7886
7888
7889 /*
7890 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7891 * attribute.
7892 */
7893 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7894 if (attr->attisdropped)
7895 return;
7896
7897 if (!attr->attnotnull)
7898 {
7900 HeapTuple tuple;
7901
7903
7905 if (!HeapTupleIsValid(tuple))
7906 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7907 attnum, RelationGetRelid(rel));
7908
7910 thisatt->attnullability = ATTNULLABLE_VALID;
7911
7912 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7913
7914 attr->attnotnull = true;
7915 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7916
7917 /*
7918 * If the nullness isn't already proven by validated constraints, have
7919 * ALTER TABLE phase 3 test for it.
7920 */
7921 if (queue_validation && wqueue &&
7923 {
7924 AlteredTableInfo *tab;
7925
7926 tab = ATGetQueueEntry(wqueue, rel);
7927 tab->verify_new_notnull = true;
7928 }
7929
7931
7933 heap_freetuple(tuple);
7934 }
7935 else
7936 {
7938 }
7939}
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition tablecmds.c:8128

References Assert, ATGetQueueEntry(), ATTNULLABLE_VALID, attnum, CacheInvalidateRelcache(), CatalogTupleUpdate(), CheckAlterTableIsSafe(), CommandCounterIncrement(), elog, ERROR, fb(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, NotNullImpliedByRelConstraints(), RelationGetDescr, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopyAttNum(), HeapTupleData::t_self, table_close(), table_open(), TupleDescAttr(), TupleDescCompactAttr(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATAddCheckNNConstraint(), ATExecSetNotNull(), DefineRelation(), and QueueNNConstraintValidation().

◆ SetIndexStorageProperties()

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

Definition at line 9156 of file tablecmds.c.

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

References attnum, CatalogTupleUpdate(), fb(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, i, index_close(), index_open(), InvokeObjectPostAlterHook, lfirst_oid, RelationGetIndexList(), RelationGetRelid, SearchSysCacheCopyAttNum(), and HeapTupleData::t_self.

Referenced by ATExecSetCompression(), and ATExecSetStorage().

◆ SetRelationHasSubclass()

void SetRelationHasSubclass ( Oid  relationId,
bool  relhassubclass 
)

Definition at line 3655 of file tablecmds.c.

3656{
3658 HeapTuple tuple;
3660
3662 ShareUpdateExclusiveLock, false) ||
3664 ShareRowExclusiveLock, true));
3665
3666 /*
3667 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3668 */
3671 if (!HeapTupleIsValid(tuple))
3672 elog(ERROR, "cache lookup failed for relation %u", relationId);
3674
3675 if (classtuple->relhassubclass != relhassubclass)
3676 {
3677 classtuple->relhassubclass = relhassubclass;
3679 }
3680 else
3681 {
3682 /* no need to change tuple, but force relcache rebuild anyway */
3684 }
3685
3686 heap_freetuple(tuple);
3688}
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition inval.c:1669

References Assert, CacheInvalidateRelcacheByTuple(), CatalogTupleUpdate(), CheckRelationOidLockedByMe(), elog, ERROR, fb(), 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 3758 of file tablecmds.c.

3761{
3763 HeapTuple tuple;
3765 Form_pg_class rd_rel;
3766 Oid reloid = RelationGetRelid(rel);
3767
3769
3770 /* Get a modifiable copy of the relation's pg_class row. */
3772
3774 if (!HeapTupleIsValid(tuple))
3775 elog(ERROR, "cache lookup failed for relation %u", reloid);
3776 otid = tuple->t_self;
3777 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3778
3779 /* Update the pg_class row. */
3780 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3783 rd_rel->relfilenode = newRelFilenumber;
3786
3787 /*
3788 * Record dependency on tablespace. This is only required for relations
3789 * that have no physical storage.
3790 */
3791 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3793 rd_rel->reltablespace);
3794
3795 heap_freetuple(tuple);
3797}
void changeDependencyOnTablespace(Oid classId, Oid objectId, Oid newTablespaceId)

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

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

◆ SplitPartitionMoveRows()

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

Definition at line 23004 of file tablecmds.c.

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

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

Referenced by ATExecSplitPartition().

◆ storage_name()

static const char * storage_name ( char  c)
static

Definition at line 2470 of file tablecmds.c.

2471{
2472 switch (c)
2473 {
2474 case TYPSTORAGE_PLAIN:
2475 return "PLAIN";
2477 return "EXTERNAL";
2479 return "EXTENDED";
2480 case TYPSTORAGE_MAIN:
2481 return "MAIN";
2482 default:
2483 return "???";
2484 }
2485}
char * c

References fb().

Referenced by MergeChildAttribute(), and MergeInheritedAttribute().

◆ StoreCatalogInheritance()

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

Definition at line 3529 of file tablecmds.c.

3531{
3532 Relation relation;
3534 ListCell *entry;
3535
3536 /*
3537 * sanity checks
3538 */
3540
3541 if (supers == NIL)
3542 return;
3543
3544 /*
3545 * Store INHERITS information in pg_inherits using direct ancestors only.
3546 * Also enter dependencies on the direct ancestors, and make sure they are
3547 * marked with relhassubclass = true.
3548 *
3549 * (Once upon a time, both direct and indirect ancestors were found here
3550 * and then entered into pg_ipl. Since that catalog doesn't exist
3551 * anymore, there's no need to look for indirect ancestors.)
3552 */
3554
3555 seqNumber = 1;
3556 foreach(entry, supers)
3557 {
3558 Oid parentOid = lfirst_oid(entry);
3559
3562 seqNumber++;
3563 }
3564
3565 table_close(relation, RowExclusiveLock);
3566}

References Assert, fb(), 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 3573 of file tablecmds.c.

3576{
3579
3580 /* store the pg_inherits row */
3582
3583 /*
3584 * Store a dependency too
3585 */
3587 parentobject.objectId = parentOid;
3588 parentobject.objectSubId = 0;
3590 childobject.objectId = relationId;
3591 childobject.objectSubId = 0;
3592
3595
3596 /*
3597 * Post creation hook of this inheritance. Since object_access_hook
3598 * doesn't take multiple object identifiers, we relay oid of parent
3599 * relation using auxiliary_id argument.
3600 */
3602 relationId, 0,
3603 parentOid, false);
3604
3605 /*
3606 * Mark the parent as having subclasses.
3607 */
3609}
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition tablecmds.c:3655

References child_dependency_type, fb(), InvokeObjectPostAlterHookArg, recordDependencyOn(), SetRelationHasSubclass(), and StoreSingleInheritance().

Referenced by CreateInheritance(), and StoreCatalogInheritance().

◆ transformColumnNameList()

static int transformColumnNameList ( Oid  relId,
List colList,
int16 attnums,
Oid atttypids,
Oid attcollids 
)
static

Definition at line 13354 of file tablecmds.c.

13356{
13357 ListCell *l;
13358 int attnum;
13359
13360 attnum = 0;
13361 foreach(l, colList)
13362 {
13363 char *attname = strVal(lfirst(l));
13366
13369 ereport(ERROR,
13371 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13372 attname)));
13374 if (attform->attnum < 0)
13375 ereport(ERROR,
13377 errmsg("system columns cannot be used in foreign keys")));
13378 if (attnum >= INDEX_MAX_KEYS)
13379 ereport(ERROR,
13381 errmsg("cannot have more than %d keys in a foreign key",
13382 INDEX_MAX_KEYS)));
13383 attnums[attnum] = attform->attnum;
13384 if (atttypids != NULL)
13385 atttypids[attnum] = attform->atttypid;
13386 if (attcollids != NULL)
13387 attcollids[attnum] = attform->attcollation;
13389 attnum++;
13390 }
13391
13392 return attnum;
13393}

References attname, attnum, ereport, errcode(), errmsg(), ERROR, fb(), GETSTRUCT(), HeapTupleIsValid, INDEX_MAX_KEYS, lfirst, ReleaseSysCache(), SearchSysCacheAttName(), and strVal.

Referenced by ATAddForeignKeyConstraint().

◆ transformFkeyCheckAttrs()

static Oid transformFkeyCheckAttrs ( Relation  pkrel,
int  numattrs,
int16 attnums,
bool  with_period,
Oid opclasses,
bool pk_has_without_overlaps 
)
static

Definition at line 13512 of file tablecmds.c.

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

References DatumGetPointer(), elog, ereport, errcode(), errmsg(), ERROR, fb(), Form_pg_index, GETSTRUCT(), heap_attisnull(), HeapTupleIsValid, i, InvalidOid, j, lfirst_oid, list_free(), ObjectIdGetDatum(), RelationGetIndexList(), RelationGetRelationName, ReleaseSysCache(), SearchSysCache1(), and SysCacheGetAttrNotNull().

Referenced by ATAddForeignKeyConstraint().

◆ transformFkeyGetPrimaryKey()

static int transformFkeyGetPrimaryKey ( Relation  pkrel,
Oid indexOid,
List **  attnamelist,
int16 attnums,
Oid atttypids,
Oid attcollids,
Oid opclasses,
bool pk_has_without_overlaps 
)
static

Definition at line 13409 of file tablecmds.c.

13413{
13420 int i;
13421
13422 /*
13423 * Get the list of index OIDs for the table from the relcache, and look up
13424 * each one in the pg_index syscache until we find one marked primary key
13425 * (hopefully there isn't more than one such). Insist it's valid, too.
13426 */
13427 *indexOid = InvalidOid;
13428
13430
13431 foreach(indexoidscan, indexoidlist)
13432 {
13433 Oid indexoid = lfirst_oid(indexoidscan);
13434
13437 elog(ERROR, "cache lookup failed for index %u", indexoid);
13439 if (indexStruct->indisprimary && indexStruct->indisvalid)
13440 {
13441 /*
13442 * Refuse to use a deferrable primary key. This is per SQL spec,
13443 * and there would be a lot of interesting semantic problems if we
13444 * tried to allow it.
13445 */
13446 if (!indexStruct->indimmediate)
13447 ereport(ERROR,
13449 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13451
13452 *indexOid = indexoid;
13453 break;
13454 }
13456 }
13457
13459
13460 /*
13461 * Check that we found it
13462 */
13463 if (!OidIsValid(*indexOid))
13464 ereport(ERROR,
13466 errmsg("there is no primary key for referenced table \"%s\"",
13468
13469 /* Must get indclass the hard way */
13473
13474 /*
13475 * Now build the list of PK attributes from the indkey definition (we
13476 * assume a primary key cannot have expressional elements)
13477 */
13478 *attnamelist = NIL;
13479 for (i = 0; i < indexStruct->indnkeyatts; i++)
13480 {
13481 int pkattno = indexStruct->indkey.values[i];
13482
13483 attnums[i] = pkattno;
13486 opclasses[i] = indclass->values[i];
13489 }
13490
13491 *pk_has_without_overlaps = indexStruct->indisexclusion;
13492
13494
13495 return i;
13496}
Oid attnumCollationId(Relation rd, int attid)
Oid attnumTypeId(Relation rd, int attid)
const NameData * attnumAttName(Relation rd, int attid)

References attnumAttName(), attnumCollationId(), attnumTypeId(), DatumGetPointer(), elog, ereport, errcode(), errmsg(), ERROR, fb(), Form_pg_index, GETSTRUCT(), HeapTupleIsValid, i, InvalidOid, lappend(), lfirst_oid, list_free(), makeString(), NameStr, NIL, ObjectIdGetDatum(), OidIsValid, pstrdup(), RelationGetIndexList(), RelationGetRelationName, ReleaseSysCache(), SearchSysCache1(), and SysCacheGetAttrNotNull().

Referenced by ATAddForeignKeyConstraint().

◆ transformPartitionSpec()

static PartitionSpec * transformPartitionSpec ( Relation  rel,
PartitionSpec partspec 
)
static

Definition at line 19752 of file tablecmds.c.

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

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), assign_expr_collations(), copyObject, ereport, errcode(), errmsg(), ERROR, EXPR_KIND_PARTITION_EXPRESSION, fb(), 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 2447 of file tablecmds.c.

2448{
2449 /*
2450 * Don't allow truncate on temp tables of other backends ... their local
2451 * buffer manager is not going to cope.
2452 */
2453 if (RELATION_IS_OTHER_TEMP(rel))
2454 ereport(ERROR,
2456 errmsg("cannot truncate temporary tables of other sessions")));
2457
2458 /*
2459 * Also check for active uses of the relation in the current transaction,
2460 * including open scans and pending AFTER trigger events.
2461 */
2462 CheckTableNotInUse(rel, "TRUNCATE");
2463}

References CheckTableNotInUse(), ereport, errcode(), errmsg(), ERROR, fb(), 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 2429 of file tablecmds.c.

2430{
2431 char *relname = NameStr(reltuple->relname);
2433
2434 /* Permissions checks */
2436 if (aclresult != ACLCHECK_OK)
2438 relname);
2439}
#define ACL_TRUNCATE
Definition parsenodes.h:80

References ACL_TRUNCATE, aclcheck_error(), ACLCHECK_OK, fb(), 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 2378 of file tablecmds.c.

2379{
2380 char *relname = NameStr(reltuple->relname);
2381
2382 /*
2383 * Only allow truncate on regular tables, foreign tables using foreign
2384 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2385 * latter are only being included here for the following checks; no
2386 * physical truncation will occur in their case.).
2387 */
2388 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2389 {
2390 Oid serverid = GetForeignServerIdByRelId(relid);
2391 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2392
2393 if (!fdwroutine->ExecForeignTruncate)
2394 ereport(ERROR,
2396 errmsg("cannot truncate foreign table \"%s\"",
2397 relname)));
2398 }
2399 else if (reltuple->relkind != RELKIND_RELATION &&
2401 ereport(ERROR,
2403 errmsg("\"%s\" is not a table", relname)));
2404
2405 /*
2406 * Most system catalogs can't be truncated at all, or at least not unless
2407 * allow_system_table_mods=on. As an exception, however, we allow
2408 * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2409 * pg_upgrade, because we need to change its relfilenode to match the old
2410 * cluster, and allowing a TRUNCATE command to be executed is the easiest
2411 * way of doing that.
2412 */
2414 && (!IsBinaryUpgrade ||
2415 (relid != LargeObjectRelationId &&
2417 ereport(ERROR,
2419 errmsg("permission denied: \"%s\" is a system catalog",
2420 relname)));
2421
2423}
bool IsBinaryUpgrade
Definition globals.c:121
#define InvokeObjectTruncateHook(objectId)

References allowSystemTableMods, ereport, errcode(), errmsg(), ERROR, FdwRoutine::ExecForeignTruncate, fb(), GetFdwRoutineByServerId(), GetForeignServerIdByRelId(), InvokeObjectTruncateHook, IsBinaryUpgrade, IsSystemClass(), NameStr, and relname.

Referenced by ExecuteTruncate(), ExecuteTruncateGuts(), and RangeVarCallbackForTruncate().

◆ tryAttachPartitionForeignKey()

static bool tryAttachPartitionForeignKey ( List **  wqueue,
ForeignKeyCacheInfo fk,
Relation  partition,
Oid  parentConstrOid,
int  numfks,
AttrNumber mapped_conkey,
AttrNumber confkey,
Oid conpfeqop,
Oid  parentInsTrigger,
Oid  parentUpdTrigger,
Relation  trigrel 
)
static

Definition at line 11721 of file tablecmds.c.

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

References AttachPartitionForeignKey(), elog, ereport, errcode(), errmsg(), ERROR, fb(), Form_pg_constraint, GETSTRUCT(), HeapTupleIsValid, i, NameStr, ObjectIdGetDatum(), OidIsValid, RelationGetRelationName, ReleaseSysCache(), and SearchSysCache1().

Referenced by addFkRecurseReferencing(), and CloneFkReferencing().

◆ TryReuseForeignKey()

static void TryReuseForeignKey ( Oid  oldId,
Constraint con 
)
static

Definition at line 15942 of file tablecmds.c.

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

References ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_HASNULL, ARR_NDIM, Assert, CONSTR_FOREIGN, Constraint::contype, DatumGetArrayTypeP, elog, ERROR, fb(), 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 15913 of file tablecmds.c.

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

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

Referenced by ATPostAlterTypeParse().

◆ validateFkOnDeleteSetColumns()

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

Definition at line 10673 of file tablecmds.c.

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

References ereport, errcode(), errmsg(), ERROR, fb(), i, j, list_nth(), and strVal.

Referenced by ATAddForeignKeyConstraint().

◆ validateForeignKeyConstraint()

static void validateForeignKeyConstraint ( char conname,
Relation  rel,
Relation  pkrel,
Oid  pkindOid,
Oid  constraintOid,
bool  hasperiod 
)
static

Definition at line 13721 of file tablecmds.c.

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

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, CHECK_FOR_INTERRUPTS, CurrentMemoryContext, DEBUG1, ereport, errmsg_internal(), ExecDropSingleTupleTableSlot(), ExecFetchSlotHeapTuple(), fb(), 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_EVENT_INSERT, TRIGGER_EVENT_ROW, TRIGGER_FIRES_ON_ORIGIN, TriggerData::type, and UnregisterSnapshot().

Referenced by ATRewriteTables().

◆ validatePartitionedIndex()

static void validatePartitionedIndex ( Relation  partedIdx,
Relation  partedTbl 
)
static

Definition at line 21808 of file tablecmds.c.

21809{
21811 SysScanDesc scan;
21813 int tuples = 0;
21815 bool updated = false;
21816
21817 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
21818
21819 /*
21820 * Scan pg_inherits for this parent index. Count each valid index we find
21821 * (verifying the pg_index entry for each), and if we reach the total
21822 * amount we expect, we can mark this parent index as valid.
21823 */
21829 NULL, 1, &key);
21830 while ((inhTup = systable_getnext(scan)) != NULL)
21831 {
21835
21837 ObjectIdGetDatum(inhForm->inhrelid));
21839 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
21841 if (indexForm->indisvalid)
21842 tuples += 1;
21844 }
21845
21846 /* Done with pg_inherits */
21847 systable_endscan(scan);
21849
21850 /*
21851 * If we found as many inherited indexes as the partitioned table has
21852 * partitions, we're good; update pg_index to set indisvalid.
21853 */
21854 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
21855 {
21859
21864 elog(ERROR, "cache lookup failed for index %u",
21867
21868 indexForm->indisvalid = true;
21869 updated = true;
21870
21872
21875 }
21876
21877 /*
21878 * If this index is in turn a partition of a larger index, validating it
21879 * might cause the parent to become valid also. Try that.
21880 */
21881 if (updated && partedIdx->rd_rel->relispartition)
21882 {
21885 Relation parentIdx,
21886 parentTbl;
21887
21888 /* make sure we see the validation we just did */
21890
21895 Assert(!parentIdx->rd_index->indisvalid);
21896
21897 validatePartitionedIndex(parentIdx, parentTbl);
21898
21901 }
21902}

References AccessExclusiveLock, AccessShareLock, Assert, BTEqualStrategyNumber, CatalogTupleUpdate(), CommandCounterIncrement(), elog, ERROR, fb(), Form_pg_index, Form_pg_inherits, get_partition_parent(), GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, ObjectIdGetDatum(), RelationData::rd_index, relation_close(), relation_open(), RelationGetPartitionDesc(), RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, ScanKeyInit(), SearchSysCache1(), SearchSysCacheCopy1, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and validatePartitionedIndex().

Referenced by ATExecAttachPartitionIdx(), and validatePartitionedIndex().

◆ verifyNotNullPKCompatible()

static void verifyNotNullPKCompatible ( HeapTuple  tuple,
const char colname 
)
static

Definition at line 9604 of file tablecmds.c.

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

References elog, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, fb(), Form_pg_constraint, get_rel_name(), GETSTRUCT(), and NameStr.

Referenced by ATPrepAddPrimaryKey().

◆ verifyPartitionIndexNotNull()

static void verifyPartitionIndexNotNull ( IndexInfo iinfo,
Relation  partition 
)
static

Definition at line 21910 of file tablecmds.c.

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

References ereport, errcode(), errdetail(), errmsg(), ERROR, fb(), i, NameStr, RelationGetDescr, RelationGetRelationName, and TupleDescAttr().

Referenced by ATExecAttachPartitionIdx().

Variable Documentation

◆ dropmsgstringarray

const struct dropmsgstrings dropmsgstringarray[]
static

Definition at line 256 of file tablecmds.c.

256 {
259 gettext_noop("table \"%s\" does not exist"),
260 gettext_noop("table \"%s\" does not exist, skipping"),
261 gettext_noop("\"%s\" is not a table"),
262 gettext_noop("Use DROP TABLE to remove a table.")},
265 gettext_noop("sequence \"%s\" does not exist"),
266 gettext_noop("sequence \"%s\" does not exist, skipping"),
267 gettext_noop("\"%s\" is not a sequence"),
268 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
271 gettext_noop("view \"%s\" does not exist"),
272 gettext_noop("view \"%s\" does not exist, skipping"),
273 gettext_noop("\"%s\" is not a view"),
274 gettext_noop("Use DROP VIEW to remove a view.")},
277 gettext_noop("materialized view \"%s\" does not exist"),
278 gettext_noop("materialized view \"%s\" does not exist, skipping"),
279 gettext_noop("\"%s\" is not a materialized view"),
280 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
283 gettext_noop("index \"%s\" does not exist"),
284 gettext_noop("index \"%s\" does not exist, skipping"),
285 gettext_noop("\"%s\" is not an index"),
286 gettext_noop("Use DROP INDEX to remove an index.")},
289 gettext_noop("type \"%s\" does not exist"),
290 gettext_noop("type \"%s\" does not exist, skipping"),
291 gettext_noop("\"%s\" is not a type"),
292 gettext_noop("Use DROP TYPE to remove a type.")},
295 gettext_noop("foreign table \"%s\" does not exist"),
296 gettext_noop("foreign table \"%s\" does not exist, skipping"),
297 gettext_noop("\"%s\" is not a foreign table"),
298 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
301 gettext_noop("table \"%s\" does not exist"),
302 gettext_noop("table \"%s\" does not exist, skipping"),
303 gettext_noop("\"%s\" is not a table"),
304 gettext_noop("Use DROP TABLE to remove a table.")},
307 gettext_noop("index \"%s\" does not exist"),
308 gettext_noop("index \"%s\" does not exist, skipping"),
309 gettext_noop("\"%s\" is not an index"),
310 gettext_noop("Use DROP INDEX to remove an index.")},
311 {'\0', 0, NULL, NULL, NULL, NULL}
312};
#define gettext_noop(x)
Definition c.h:1213

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits