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/tupconvert.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_extension_d.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/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/repack.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  PartitionIndexExtDepEntry
 
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 PartitionIndexExtDepEntry PartitionIndexExtDepEntry
 
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 ATExecAlterFKConstrEnforceability (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 ATExecAlterCheckConstrEnforceability (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)
 
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 AlterFKConstrEnforceabilityRecurse (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 AlterCheckConstrEnforceabilityRecurse (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Oid conrelid, bool recurse, bool recursing, LOCKMODE lockmode)
 
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 ATPrepChangeInherit (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)
 
static ListcollectPartitionIndexExtDeps (List *partitionOids)
 
static void applyPartitionIndexExtDeps (Oid newPartOid, List *extDepState)
 
static void freePartitionIndexExtDeps (List *extDepState)
 
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 bool equal_oid_lists (const List *a, const List *b)
 
static int cmp_partition_index_ext_dep (const ListCell *a, const ListCell *b)
 
static SplitPartitionContextcreateSplitPartitionContext (Relation partRel)
 
static void deleteSplitPartitionContext (SplitPartitionContext *pc, List **wqueue, uint32 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 170 of file tablecmds.c.

◆ ATT_COMPOSITE_TYPE

#define ATT_COMPOSITE_TYPE   0x0010

Definition at line 342 of file tablecmds.c.

◆ ATT_FOREIGN_TABLE

#define ATT_FOREIGN_TABLE   0x0020

Definition at line 343 of file tablecmds.c.

◆ ATT_INDEX

#define ATT_INDEX   0x0008

Definition at line 341 of file tablecmds.c.

◆ ATT_MATVIEW

#define ATT_MATVIEW   0x0004

Definition at line 340 of file tablecmds.c.

◆ ATT_PARTITIONED_INDEX

#define ATT_PARTITIONED_INDEX   0x0040

Definition at line 344 of file tablecmds.c.

◆ ATT_PARTITIONED_TABLE

#define ATT_PARTITIONED_TABLE   0x0100

Definition at line 346 of file tablecmds.c.

◆ ATT_SEQUENCE

#define ATT_SEQUENCE   0x0080

Definition at line 345 of file tablecmds.c.

◆ ATT_TABLE

#define ATT_TABLE   0x0001

Definition at line 338 of file tablecmds.c.

◆ ATT_VIEW

#define ATT_VIEW   0x0002

Definition at line 339 of file tablecmds.c.

◆ child_dependency_type

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

Definition at line 396 of file tablecmds.c.

@ DEPENDENCY_NORMAL
Definition dependency.h:33

Typedef Documentation

◆ addFkConstraintSides

◆ AlteredTableInfo

◆ AlterTablePass

◆ ForeignTruncateInfo

◆ NewColumnValue

◆ NewConstraint

◆ OnCommitItem

◆ PartitionIndexExtDepEntry

◆ SplitPartitionContext

Enumeration Type Documentation

◆ addFkConstraintSides

Enumerator
addFkReferencedSide 
addFkReferencingSide 
addFkBothSides 

Definition at line 363 of file tablecmds.c.

364{
addFkConstraintSides
Definition tablecmds.c:364
@ addFkReferencingSide
Definition tablecmds.c:366
@ addFkBothSides
Definition tablecmds.c:367
@ addFkReferencedSide
Definition tablecmds.c:365

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

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

Function Documentation

◆ add_column_collation_dependency()

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

Definition at line 7807 of file tablecmds.c.

7808{
7810 referenced;
7811
7812 /* We know the default collation is pinned, so don't bother recording it */
7814 {
7816 myself.objectId = relid;
7817 myself.objectSubId = attnum;
7819 referenced.objectId = collid;
7820 referenced.objectSubId = 0;
7822 }
7823}
#define OidIsValid(objectId)
Definition c.h:858
Oid collid
int16 attnum
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition pg_depend.c:51
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 7789 of file tablecmds.c.

7790{
7792 referenced;
7793
7795 myself.objectId = relid;
7796 myself.objectSubId = attnum;
7797 referenced.classId = TypeRelationId;
7798 referenced.objectId = typid;
7799 referenced.objectSubId = 0;
7801}

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

10807{
10808 ObjectAddress address;
10809 Oid constrOid;
10810 char *conname;
10811 bool conislocal;
10813 bool connoinherit;
10814
10815 /*
10816 * Verify relkind for each referenced partition. At the top level, this
10817 * is redundant with a previous check, but we need it when recursing.
10818 */
10819 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10820 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10821 ereport(ERROR,
10823 errmsg("referenced relation \"%s\" is not a table",
10825
10826 /*
10827 * Caller supplies us with a constraint name; however, it may be used in
10828 * this partition, so come up with a different one in that case. Unless
10829 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10830 * supplied name with an underscore and digit(s) appended.
10831 */
10833 RelationGetRelid(rel),
10834 constraintname))
10835 conname = ChooseConstraintName(constraintname,
10836 NULL,
10837 "",
10839 else
10840 conname = constraintname;
10841
10842 if (fkconstraint->conname == NULL)
10843 fkconstraint->conname = pstrdup(conname);
10844
10846 {
10847 conislocal = false;
10848 coninhcount = 1;
10849 connoinherit = false;
10850 }
10851 else
10852 {
10853 conislocal = true;
10854 coninhcount = 0;
10855
10856 /*
10857 * always inherit for partitioned tables, never for legacy inheritance
10858 */
10860 }
10861
10862 /*
10863 * Record the FK constraint in pg_constraint.
10864 */
10868 fkconstraint->deferrable,
10869 fkconstraint->initdeferred,
10870 fkconstraint->is_enforced,
10871 fkconstraint->initially_valid,
10873 RelationGetRelid(rel),
10874 fkattnum,
10875 numfks,
10876 numfks,
10877 InvalidOid, /* not a domain constraint */
10878 indexOid,
10880 pkattnum,
10884 numfks,
10885 fkconstraint->fk_upd_action,
10886 fkconstraint->fk_del_action,
10889 fkconstraint->fk_matchtype,
10890 NULL, /* no exclusion constraint */
10891 NULL, /* no check constraint */
10892 NULL,
10893 conislocal, /* islocal */
10894 coninhcount, /* inhcount */
10895 connoinherit, /* conNoInherit */
10896 with_period, /* conPeriod */
10897 is_internal); /* is_internal */
10898
10900
10901 /*
10902 * In partitioning cases, create the dependency entries for this
10903 * constraint. (For non-partitioned cases, relevant entries were created
10904 * by CreateConstraintEntry.)
10905 *
10906 * On the referenced side, we need the constraint to have an internal
10907 * dependency on its parent constraint; this means that this constraint
10908 * cannot be dropped on its own -- only through the parent constraint. It
10909 * also means the containing partition cannot be dropped on its own, but
10910 * it can be detached, at which point this dependency is removed (after
10911 * verifying that no rows are referenced via this FK.)
10912 *
10913 * When processing the referencing side, we link the constraint via the
10914 * special partitioning dependencies: the parent constraint is the primary
10915 * dependent, and the partition on which the foreign key exists is the
10916 * secondary dependency. That way, this constraint is dropped if either
10917 * of these objects is.
10918 *
10919 * Note that this is only necessary for the subsidiary pg_constraint rows
10920 * in partitions; the topmost row doesn't need any of this.
10921 */
10923 {
10925
10927
10931 else
10932 {
10936 }
10937 }
10938
10939 /* make new constraint visible, in case we add more */
10941
10942 return address;
10943}
#define Assert(condition)
Definition c.h:943
int16_t int16
Definition c.h:619
@ 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:875
#define ERROR
Definition elog.h:40
#define ereport(elevel,...)
Definition elog.h:152
char * pstrdup(const char *in)
Definition mcxt.c:1910
static char * errmsg
#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:516
#define RelationGetRelationName(relation)
Definition rel.h:550
#define RelationGetNamespace(relation)
Definition rel.h:557
Form_pg_class rd_rel
Definition rel.h:111
void CommandCounterIncrement(void)
Definition xact.c:1130

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

10987{
10990
10993
10994 /*
10995 * Create action triggers to enforce the constraint, or skip them if the
10996 * constraint is NOT ENFORCED.
10997 */
10998 if (fkconstraint->is_enforced)
11002 parentConstr, indexOid,
11005
11006 /*
11007 * If the referenced table is partitioned, recurse on ourselves to handle
11008 * each partition. We need one pg_constraint row created for each
11009 * partition in addition to the pg_constraint row for the parent table.
11010 */
11011 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11012 {
11014
11015 for (int i = 0; i < pd->nparts; i++)
11016 {
11017 Relation partRel;
11018 AttrMap *map;
11021 ObjectAddress address;
11022
11023 /* XXX would it be better to acquire these locks beforehand? */
11024 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
11025
11026 /*
11027 * Map the attribute numbers in the referenced side of the FK
11028 * definition to match the partition's column layout.
11029 */
11032 false);
11033 if (map)
11034 {
11036 for (int j = 0; j < numfks; j++)
11037 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
11038 }
11039 else
11041
11042 /* Determine the index to use at this level */
11043 partIndexId = index_get_partition(partRel, indexOid);
11044 if (!OidIsValid(partIndexId))
11045 elog(ERROR, "index for %u not found in partition %s",
11046 indexOid, RelationGetRelationName(partRel));
11047
11048 /* Create entry at this level ... */
11050 fkconstraint->conname, fkconstraint, rel,
11051 partRel, partIndexId, parentConstr,
11055 fkdelsetcols, true, with_period);
11056 /* ... and recurse to our children */
11058 partIndexId, address.objectId, numfks,
11064 with_period);
11065
11066 /* Done -- clean up (but keep the lock) */
11067 table_close(partRel, NoLock);
11068 if (map)
11069 {
11071 free_attrmap(map);
11072 }
11073 }
11074 }
11075}
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:228
#define palloc_array(type, count)
Definition fe_memutils.h:91
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:1619
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:542
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 11116 of file tablecmds.c.

11124{
11127
11131
11132 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11133 ereport(ERROR,
11135 errmsg("foreign key constraints are not supported on foreign tables")));
11136
11137 /*
11138 * Add check triggers if the constraint is ENFORCED, and if needed,
11139 * schedule them to be checked in Phase 3.
11140 *
11141 * If the relation is partitioned, drill down to do it to its partitions.
11142 */
11143 if (fkconstraint->is_enforced)
11148 indexOid,
11151
11152 if (rel->rd_rel->relkind == RELKIND_RELATION)
11153 {
11154 /*
11155 * Tell Phase 3 to check that the constraint is satisfied by existing
11156 * rows. We can skip this during table creation, when constraint is
11157 * specified as NOT ENFORCED, or when requested explicitly by
11158 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11159 * recreating a constraint following a SET DATA TYPE operation that
11160 * did not impugn its validity.
11161 */
11162 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11163 fkconstraint->is_enforced)
11164 {
11166 AlteredTableInfo *tab;
11167
11168 tab = ATGetQueueEntry(wqueue, rel);
11169
11172 newcon->contype = CONSTR_FOREIGN;
11173 newcon->refrelid = RelationGetRelid(pkrel);
11174 newcon->refindid = indexOid;
11175 newcon->conid = parentConstr;
11176 newcon->conwithperiod = fkconstraint->fk_with_period;
11177 newcon->qual = (Node *) fkconstraint;
11178
11179 tab->constraints = lappend(tab->constraints, newcon);
11180 }
11181 }
11182 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11183 {
11186
11187 /*
11188 * Triggers of the foreign keys will be manipulated a bunch of times
11189 * in the loop below. To avoid repeatedly opening/closing the trigger
11190 * catalog relation, we open it here and pass it to the subroutines
11191 * called below.
11192 */
11194
11195 /*
11196 * Recurse to take appropriate action on each partition; either we
11197 * find an existing constraint to reparent to ours, or we create a new
11198 * one.
11199 */
11200 for (int i = 0; i < pd->nparts; i++)
11201 {
11202 Relation partition = table_open(pd->oids[i], lockmode);
11203 List *partFKs;
11204 AttrMap *attmap;
11206 bool attached;
11207 ObjectAddress address;
11208
11210
11212 RelationGetDescr(rel),
11213 false);
11214 for (int j = 0; j < numfks; j++)
11215 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11216
11217 /* Check whether an existing constraint can be repurposed */
11219 attached = false;
11221 {
11223 fk,
11224 partition,
11226 numfks,
11228 pkattnum,
11232 trigrel))
11233 {
11234 attached = true;
11235 break;
11236 }
11237 }
11238 if (attached)
11239 {
11241 continue;
11242 }
11243
11244 /*
11245 * No luck finding a good constraint to reuse; create our own.
11246 */
11248 fkconstraint->conname, fkconstraint,
11249 partition, pkrel, indexOid, parentConstr,
11254 with_period);
11255
11256 /* call ourselves to finalize the creation and we're done */
11258 indexOid,
11259 address.objectId,
11260 numfks,
11261 pkattnum,
11269 lockmode,
11272 with_period);
11273
11275 }
11276
11278 }
11279}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition attmap.c:175
#define palloc0_object(type)
Definition fe_memutils.h:90
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:1299
#define copyObject(obj)
Definition nodes.h:232
@ CONSTR_FOREIGN
#define INDEX_MAX_KEYS
#define foreach_node(type, var, lst)
Definition pg_list.h:528
List * RelationGetFKeyList(Relation relation)
Definition relcache.c:4732
Definition pg_list.h:54
Definition nodes.h:135
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition tablecmds.c:6648
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:4506
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 6682 of file tablecmds.c.

6683{
6684 switch (cmdtype)
6685 {
6686 case AT_AddColumn:
6687 case AT_AddColumnToView:
6688 return "ADD COLUMN";
6689 case AT_ColumnDefault:
6691 return "ALTER COLUMN ... SET DEFAULT";
6692 case AT_DropNotNull:
6693 return "ALTER COLUMN ... DROP NOT NULL";
6694 case AT_SetNotNull:
6695 return "ALTER COLUMN ... SET NOT NULL";
6696 case AT_SetExpression:
6697 return "ALTER COLUMN ... SET EXPRESSION";
6698 case AT_DropExpression:
6699 return "ALTER COLUMN ... DROP EXPRESSION";
6700 case AT_SetStatistics:
6701 return "ALTER COLUMN ... SET STATISTICS";
6702 case AT_SetOptions:
6703 return "ALTER COLUMN ... SET";
6704 case AT_ResetOptions:
6705 return "ALTER COLUMN ... RESET";
6706 case AT_SetStorage:
6707 return "ALTER COLUMN ... SET STORAGE";
6708 case AT_SetCompression:
6709 return "ALTER COLUMN ... SET COMPRESSION";
6710 case AT_DropColumn:
6711 return "DROP COLUMN";
6712 case AT_AddIndex:
6713 case AT_ReAddIndex:
6714 return NULL; /* not real grammar */
6715 case AT_AddConstraint:
6716 case AT_ReAddConstraint:
6719 return "ADD CONSTRAINT";
6720 case AT_AlterConstraint:
6721 return "ALTER CONSTRAINT";
6723 return "VALIDATE CONSTRAINT";
6724 case AT_DropConstraint:
6725 return "DROP CONSTRAINT";
6726 case AT_ReAddComment:
6727 return NULL; /* not real grammar */
6728 case AT_AlterColumnType:
6729 return "ALTER COLUMN ... SET DATA TYPE";
6731 return "ALTER COLUMN ... OPTIONS";
6732 case AT_ChangeOwner:
6733 return "OWNER TO";
6734 case AT_ClusterOn:
6735 return "CLUSTER ON";
6736 case AT_DropCluster:
6737 return "SET WITHOUT CLUSTER";
6738 case AT_SetAccessMethod:
6739 return "SET ACCESS METHOD";
6740 case AT_SetLogged:
6741 return "SET LOGGED";
6742 case AT_SetUnLogged:
6743 return "SET UNLOGGED";
6744 case AT_DropOids:
6745 return "SET WITHOUT OIDS";
6746 case AT_SetTableSpace:
6747 return "SET TABLESPACE";
6748 case AT_SetRelOptions:
6749 return "SET";
6750 case AT_ResetRelOptions:
6751 return "RESET";
6753 return NULL; /* not real grammar */
6754 case AT_EnableTrig:
6755 return "ENABLE TRIGGER";
6757 return "ENABLE ALWAYS TRIGGER";
6759 return "ENABLE REPLICA TRIGGER";
6760 case AT_DisableTrig:
6761 return "DISABLE TRIGGER";
6762 case AT_EnableTrigAll:
6763 return "ENABLE TRIGGER ALL";
6764 case AT_DisableTrigAll:
6765 return "DISABLE TRIGGER ALL";
6766 case AT_EnableTrigUser:
6767 return "ENABLE TRIGGER USER";
6768 case AT_DisableTrigUser:
6769 return "DISABLE TRIGGER USER";
6770 case AT_EnableRule:
6771 return "ENABLE RULE";
6773 return "ENABLE ALWAYS RULE";
6775 return "ENABLE REPLICA RULE";
6776 case AT_DisableRule:
6777 return "DISABLE RULE";
6778 case AT_AddInherit:
6779 return "INHERIT";
6780 case AT_DropInherit:
6781 return "NO INHERIT";
6782 case AT_AddOf:
6783 return "OF";
6784 case AT_DropOf:
6785 return "NOT OF";
6786 case AT_ReplicaIdentity:
6787 return "REPLICA IDENTITY";
6789 return "ENABLE ROW SECURITY";
6791 return "DISABLE ROW SECURITY";
6793 return "FORCE ROW SECURITY";
6795 return "NO FORCE ROW SECURITY";
6796 case AT_GenericOptions:
6797 return "OPTIONS";
6798 case AT_AttachPartition:
6799 return "ATTACH PARTITION";
6800 case AT_DetachPartition:
6801 return "DETACH PARTITION";
6803 return "DETACH PARTITION ... FINALIZE";
6804 case AT_MergePartitions:
6805 return "MERGE PARTITIONS";
6806 case AT_SplitPartition:
6807 return "SPLIT PARTITION";
6808 case AT_AddIdentity:
6809 return "ALTER COLUMN ... ADD IDENTITY";
6810 case AT_SetIdentity:
6811 return "ALTER COLUMN ... SET";
6812 case AT_DropIdentity:
6813 return "ALTER COLUMN ... DROP IDENTITY";
6814 case AT_ReAddStatistics:
6815 return NULL; /* not real grammar */
6816 }
6817
6818 return NULL;
6819}
@ 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().

◆ AlterCheckConstrEnforceabilityRecurse()

static void AlterCheckConstrEnforceabilityRecurse ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
Oid  conrelid,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode 
)
static

Definition at line 12763 of file tablecmds.c.

12767{
12770 ScanKeyData skey[3];
12771
12772 ScanKeyInit(&skey[0],
12775 ObjectIdGetDatum(conrelid));
12776 ScanKeyInit(&skey[1],
12780 ScanKeyInit(&skey[2],
12783 CStringGetDatum(cmdcon->conname));
12784
12786 NULL, 3, skey);
12787
12789 ereport(ERROR,
12791 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12792 cmdcon->conname, get_rel_name(conrelid)));
12793
12795 recurse, recursing, lockmode);
12796
12798}
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
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
char * get_rel_name(Oid relid)
Definition lsyscache.c:2234
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
static Datum CStringGetDatum(const char *X)
Definition postgres.h:383
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
#define BTEqualStrategyNumber
Definition stratnum.h:31
static bool ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple, bool recurse, bool recursing, LOCKMODE lockmode)

References ATExecAlterCheckConstrEnforceability(), BTEqualStrategyNumber, CStringGetDatum(), ereport, errcode(), errmsg, ERROR, fb(), get_rel_name(), HeapTupleIsValid, InvalidOid, ObjectIdGetDatum(), ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by ATExecAlterCheckConstrEnforceability().

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

13069{
13071 Oid conoid;
13072 ScanKeyData pkey;
13075
13077 conoid = currcon->oid;
13078
13079 ScanKeyInit(&pkey,
13082 ObjectIdGetDatum(conoid));
13083
13085 true, NULL, 1, &pkey);
13086
13088 {
13091
13092 childrel = table_open(childcon->conrelid, lockmode);
13093
13095 childtup, recurse, otherrelids, lockmode);
13097 }
13098
13100}
static void * GETSTRUCT(const HeapTupleData *tuple)
END_CATALOG_STRUCT typedef FormData_pg_constraint * Form_pg_constraint
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().

◆ AlterConstrTriggerDeferrability()

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

Definition at line 12947 of file tablecmds.c.

12950{
12954
12958 ObjectIdGetDatum(conoid));
12960 NULL, 1, &tgkey);
12962 {
12966
12967 /*
12968 * Remember OIDs of other relation(s) involved in FK constraint.
12969 * (Note: it's likely that we could skip forcing a relcache inval for
12970 * other rels that don't have a trigger whose properties change, but
12971 * let's be conservative.)
12972 */
12973 if (tgform->tgrelid != RelationGetRelid(rel))
12975 tgform->tgrelid);
12976
12977 /*
12978 * Update enable status and deferrability of RI_FKey_noaction_del,
12979 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12980 * triggers, but not others; see createForeignKeyActionTriggers and
12981 * CreateFKCheckTrigger.
12982 */
12983 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12984 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12985 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12986 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12987 continue;
12988
12991
12992 copy_tg->tgdeferrable = deferrable;
12993 copy_tg->tginitdeferred = initdeferred;
12995
12997
12999 }
13000
13002}
HeapTuple heap_copytuple(HeapTuple tuple)
Definition heaptuple.c:686
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
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 13107 of file tablecmds.c.

13109{
13112
13113 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
13114 cmdcon->alterInheritability);
13115
13118
13119 if (cmdcon->alterEnforceability)
13120 {
13121 copy_con->conenforced = cmdcon->is_enforced;
13122
13123 /*
13124 * NB: The convalidated status is irrelevant when the constraint is
13125 * set to NOT ENFORCED, but for consistency, it should still be set
13126 * appropriately. Similarly, if the constraint is later changed to
13127 * ENFORCED, validation will be performed during phase 3, so it makes
13128 * sense to mark it as valid in that case.
13129 */
13130 copy_con->convalidated = cmdcon->is_enforced;
13131 }
13132 if (cmdcon->alterDeferrability)
13133 {
13134 copy_con->condeferrable = cmdcon->deferrable;
13135 copy_con->condeferred = cmdcon->initdeferred;
13136 }
13137 if (cmdcon->alterInheritability)
13138 copy_con->connoinherit = cmdcon->noinherit;
13139
13142
13143 /* Make new constraint flags visible to others */
13145
13147}
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition inval.c:1688

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

Referenced by ATExecAlterCheckConstrEnforceability(), ATExecAlterConstrDeferrability(), ATExecAlterConstrInheritability(), and ATExecAlterFKConstrEnforceability().

◆ AlterFKConstrEnforceabilityRecurse()

static void AlterFKConstrEnforceabilityRecurse ( 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 13016 of file tablecmds.c.

13024{
13026 Oid conoid;
13027 ScanKeyData pkey;
13030
13032 conoid = currcon->oid;
13033
13034 ScanKeyInit(&pkey,
13037 ObjectIdGetDatum(conoid));
13038
13040 true, NULL, 1, &pkey);
13041
13044 pkrelid, childtup, lockmode,
13049
13051}
static bool ATExecAlterFKConstrEnforceability(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 ATExecAlterFKConstrEnforceability(), BTEqualStrategyNumber, fb(), Form_pg_constraint, GETSTRUCT(), HeapTupleIsValid, ObjectIdGetDatum(), ScanKeyInit(), systable_beginscan(), systable_endscan(), and systable_getnext().

Referenced by ATExecAlterFKConstrEnforceability().

◆ AlterIndexNamespaces()

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

Definition at line 19379 of file tablecmds.c.

19381{
19382 List *indexList;
19383 ListCell *l;
19384
19386
19387 foreach(l, indexList)
19388 {
19389 Oid indexOid = lfirst_oid(l);
19391
19393 thisobj.objectId = indexOid;
19394 thisobj.objectSubId = 0;
19395
19396 /*
19397 * Note: currently, the index will not have its own dependency on the
19398 * namespace, so we don't need to do changeDependencyFor(). There's no
19399 * row type in pg_type, either.
19400 *
19401 * XXX this objsMoved test may be pointless -- surely we have a single
19402 * dependency link from a relation to each index?
19403 */
19405 {
19408 false, objsMoved);
19410 }
19411 }
19412
19414}
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:4837
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 19302 of file tablecmds.c.

19306{
19310 bool already_done = false;
19311
19312 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19315 elog(ERROR, "cache lookup failed for relation %u", relOid);
19317
19318 Assert(classForm->relnamespace == oldNspOid);
19319
19320 thisobj.classId = RelationRelationId;
19321 thisobj.objectId = relOid;
19322 thisobj.objectSubId = 0;
19323
19324 /*
19325 * If the object has already been moved, don't move it again. If it's
19326 * already in the right place, don't move it, but still fire the object
19327 * access hook.
19328 */
19330 if (!already_done && oldNspOid != newNspOid)
19331 {
19332 ItemPointerData otid = classTup->t_self;
19333
19334 /* check for duplicate name (more friendly than unique-index failure) */
19335 if (get_relname_relid(NameStr(classForm->relname),
19337 ereport(ERROR,
19339 errmsg("relation \"%s\" already exists in schema \"%s\"",
19340 NameStr(classForm->relname),
19342
19343 /* classTup is a copy, so OK to scribble on */
19344 classForm->relnamespace = newNspOid;
19345
19348
19349
19350 /* Update dependency on schema if caller said so */
19351 if (hasDependEntry &&
19353 relOid,
19355 oldNspOid,
19356 newNspOid) != 1)
19357 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19358 NameStr(classForm->relname));
19359 }
19360 else
19362 if (!already_done)
19363 {
19365
19367 }
19368
19370}
#define NameStr(name)
Definition c.h:835
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:3674
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition lsyscache.c:2191
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:470
HeapTuple SearchSysCacheLockedCopy1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:400

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

19427{
19429 SysScanDesc scan;
19430 ScanKeyData key[2];
19431 HeapTuple tup;
19432
19433 /*
19434 * SERIAL sequences are those having an auto dependency on one of the
19435 * table's columns (we don't care *which* column, exactly).
19436 */
19438
19439 ScanKeyInit(&key[0],
19443 ScanKeyInit(&key[1],
19447 /* we leave refobjsubid unspecified */
19448
19450 NULL, 2, key);
19451
19452 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19453 {
19456
19457 /* skip dependencies other than auto dependencies on columns */
19458 if (depForm->refobjsubid == 0 ||
19459 depForm->classid != RelationRelationId ||
19460 depForm->objsubid != 0 ||
19461 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19462 continue;
19463
19464 /* Use relation_open just in case it's an index */
19465 seqRel = relation_open(depForm->objid, lockmode);
19466
19467 /* skip non-sequence relations */
19468 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19469 {
19470 /* No need to keep the lock */
19471 relation_close(seqRel, lockmode);
19472 continue;
19473 }
19474
19475 /* Fix the pg_class and pg_depend entries */
19478 true, objsMoved);
19479
19480 /*
19481 * Sequences used to have entries in pg_type, but no longer do. If we
19482 * ever re-instate that, we'll need to move the pg_type entry to the
19483 * new namespace, too (using AlterTypeNamespaceInternal).
19484 */
19486
19487 /* Now we can close it. Keep the lock till end of transaction. */
19489 }
19490
19491 systable_endscan(scan);
19492
19494}
@ 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:510
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:206
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:48

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

4593{
4594 Relation rel;
4595
4596 /* Caller is required to provide an adequate lock. */
4597 rel = relation_open(context->relid, NoLock);
4598
4600
4601 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4602}
#define stmt
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:4932

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4665 of file tablecmds.c.

4666{
4667 /*
4668 * This only works if we read catalog tables using MVCC snapshots.
4669 */
4670 ListCell *lcmd;
4672
4673 foreach(lcmd, cmds)
4674 {
4676 LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */
4677
4678 switch (cmd->subtype)
4679 {
4680 /*
4681 * These subcommands rewrite the heap, so require full locks.
4682 */
4683 case AT_AddColumn: /* may rewrite heap, in some cases and visible
4684 * to SELECT */
4685 case AT_SetAccessMethod: /* must rewrite heap */
4686 case AT_SetTableSpace: /* must rewrite heap */
4687 case AT_AlterColumnType: /* must rewrite heap */
4689 break;
4690
4691 /*
4692 * These subcommands may require addition of toast tables. If
4693 * we add a toast table to a table currently being scanned, we
4694 * might miss data added to the new toast table by concurrent
4695 * insert transactions.
4696 */
4697 case AT_SetStorage: /* may add toast tables, see
4698 * ATRewriteCatalogs() */
4700 break;
4701
4702 /*
4703 * Removing constraints can affect SELECTs that have been
4704 * optimized assuming the constraint holds true. See also
4705 * CloneFkReferenced.
4706 */
4707 case AT_DropConstraint: /* as DROP INDEX */
4708 case AT_DropNotNull: /* may change some SQL plans */
4710 break;
4711
4712 /*
4713 * Subcommands that may be visible to concurrent SELECTs
4714 */
4715 case AT_DropColumn: /* change visible to SELECT */
4716 case AT_AddColumnToView: /* CREATE VIEW */
4717 case AT_DropOids: /* used to equiv to DropColumn */
4718 case AT_EnableAlwaysRule: /* may change SELECT rules */
4719 case AT_EnableReplicaRule: /* may change SELECT rules */
4720 case AT_EnableRule: /* may change SELECT rules */
4721 case AT_DisableRule: /* may change SELECT rules */
4723 break;
4724
4725 /*
4726 * Changing owner may remove implicit SELECT privileges
4727 */
4728 case AT_ChangeOwner: /* change visible to SELECT */
4730 break;
4731
4732 /*
4733 * Changing foreign table options may affect optimization.
4734 */
4735 case AT_GenericOptions:
4738 break;
4739
4740 /*
4741 * These subcommands affect write operations only.
4742 */
4743 case AT_EnableTrig:
4746 case AT_EnableTrigAll:
4747 case AT_EnableTrigUser:
4748 case AT_DisableTrig:
4749 case AT_DisableTrigAll:
4750 case AT_DisableTrigUser:
4752 break;
4753
4754 /*
4755 * These subcommands affect write operations only. XXX
4756 * Theoretically, these could be ShareRowExclusiveLock.
4757 */
4758 case AT_ColumnDefault:
4760 case AT_AlterConstraint:
4761 case AT_AddIndex: /* from ADD CONSTRAINT */
4763 case AT_ReplicaIdentity:
4764 case AT_SetNotNull:
4769 case AT_AddIdentity:
4770 case AT_DropIdentity:
4771 case AT_SetIdentity:
4772 case AT_SetExpression:
4773 case AT_DropExpression:
4774 case AT_SetCompression:
4776 break;
4777
4778 case AT_AddConstraint:
4779 case AT_ReAddConstraint: /* becomes AT_AddConstraint */
4780 case AT_ReAddDomainConstraint: /* becomes AT_AddConstraint */
4781 if (IsA(cmd->def, Constraint))
4782 {
4783 Constraint *con = (Constraint *) cmd->def;
4784
4785 switch (con->contype)
4786 {
4787 case CONSTR_EXCLUSION:
4788 case CONSTR_PRIMARY:
4789 case CONSTR_UNIQUE:
4790
4791 /*
4792 * Cases essentially the same as CREATE INDEX. We
4793 * could reduce the lock strength to ShareLock if
4794 * we can work out how to allow concurrent catalog
4795 * updates. XXX Might be set down to
4796 * ShareRowExclusiveLock but requires further
4797 * analysis.
4798 */
4800 break;
4801 case CONSTR_FOREIGN:
4802
4803 /*
4804 * We add triggers to both tables when we add a
4805 * Foreign Key, so the lock level must be at least
4806 * as strong as CREATE TRIGGER.
4807 */
4809 break;
4810
4811 default:
4813 }
4814 }
4815 break;
4816
4817 /*
4818 * These subcommands affect inheritance behaviour. Queries
4819 * started before us will continue to see the old inheritance
4820 * behaviour, while queries started after we commit will see
4821 * new behaviour. No need to prevent reads or writes to the
4822 * subtable while we hook it up though. Changing the TupDesc
4823 * may be a problem, so keep highest lock.
4824 */
4825 case AT_AddInherit:
4826 case AT_DropInherit:
4828 break;
4829
4830 /*
4831 * These subcommands affect implicit row type conversion. They
4832 * have affects similar to CREATE/DROP CAST on queries. don't
4833 * provide for invalidating parse trees as a result of such
4834 * changes, so we keep these at AccessExclusiveLock.
4835 */
4836 case AT_AddOf:
4837 case AT_DropOf:
4839 break;
4840
4841 /*
4842 * Only used by CREATE OR REPLACE VIEW which must conflict
4843 * with an SELECTs currently using the view.
4844 */
4847 break;
4848
4849 /*
4850 * These subcommands affect general strategies for performance
4851 * and maintenance, though don't change the semantic results
4852 * from normal data reads and writes. Delaying an ALTER TABLE
4853 * behind currently active writes only delays the point where
4854 * the new strategy begins to take effect, so there is no
4855 * benefit in waiting. In this case the minimum restriction
4856 * applies: we don't currently allow concurrent catalog
4857 * updates.
4858 */
4859 case AT_SetStatistics: /* Uses MVCC in getTableAttrs() */
4860 case AT_ClusterOn: /* Uses MVCC in getIndexes() */
4861 case AT_DropCluster: /* Uses MVCC in getIndexes() */
4862 case AT_SetOptions: /* Uses MVCC in getTableAttrs() */
4863 case AT_ResetOptions: /* Uses MVCC in getTableAttrs() */
4865 break;
4866
4867 case AT_SetLogged:
4868 case AT_SetUnLogged:
4870 break;
4871
4872 case AT_ValidateConstraint: /* Uses MVCC in getConstraints() */
4874 break;
4875
4876 /*
4877 * Rel options are more complex than first appears. Options
4878 * are set here for tables, views and indexes; for historical
4879 * reasons these can all be used with ALTER TABLE, so we can't
4880 * decide between them using the basic grammar.
4881 */
4882 case AT_SetRelOptions: /* Uses MVCC in getIndexes() and
4883 * getTables() */
4884 case AT_ResetRelOptions: /* Uses MVCC in getIndexes() and
4885 * getTables() */
4887 break;
4888
4889 case AT_AttachPartition:
4891 break;
4892
4893 case AT_DetachPartition:
4894 if (((PartitionCmd *) cmd->def)->concurrent)
4896 else
4898 break;
4899
4902 break;
4903
4904 case AT_MergePartitions:
4905 case AT_SplitPartition:
4907 break;
4908
4909 default: /* oops */
4910 elog(ERROR, "unrecognized alter table type: %d",
4911 (int) cmd->subtype);
4912 break;
4913 }
4914
4915 /*
4916 * Take the greatest lockmode from any subcommand
4917 */
4918 if (cmd_lockmode > lockmode)
4919 lockmode = cmd_lockmode;
4920 }
4921
4922 return lockmode;
4923}
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 4620 of file tablecmds.c.

4621{
4622 Relation rel;
4623 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4624
4625 rel = relation_open(relid, lockmode);
4626
4628
4629 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4630}
void EventTriggerAlterTableRelid(Oid objectId)
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition tablecmds.c:4665

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4532 of file tablecmds.c.

4533{
4534 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4535 stmt->missing_ok ? RVR_MISSING_OK : 0,
4537 stmt);
4538}
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 17242 of file tablecmds.c.

17243{
17244 List *relations = NIL;
17245 ListCell *l;
17246 ScanKeyData key[1];
17247 Relation rel;
17248 TableScanDesc scan;
17249 HeapTuple tuple;
17252 List *role_oids = roleSpecsToIds(stmt->roles);
17253
17254 /* Ensure we were not asked to move something we can't */
17255 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17256 stmt->objtype != OBJECT_MATVIEW)
17257 ereport(ERROR,
17259 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17260
17261 /* Get the orig and new tablespace OIDs */
17262 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17263 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17264
17265 /* Can't move shared relations in to or out of pg_global */
17266 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17269 ereport(ERROR,
17271 errmsg("cannot move relations in to or out of pg_global tablespace")));
17272
17273 /*
17274 * Must have CREATE rights on the new tablespace, unless it is the
17275 * database default tablespace (which all users implicitly have CREATE
17276 * rights on).
17277 */
17279 {
17281
17283 ACL_CREATE);
17284 if (aclresult != ACLCHECK_OK)
17287 }
17288
17289 /*
17290 * Now that the checks are done, check if we should set either to
17291 * InvalidOid because it is our database's default tablespace.
17292 */
17295
17298
17299 /* no-op */
17301 return new_tablespaceoid;
17302
17303 /*
17304 * Walk the list of objects in the tablespace and move them. This will
17305 * only find objects in our database, of course.
17306 */
17307 ScanKeyInit(&key[0],
17311
17313 scan = table_beginscan_catalog(rel, 1, key);
17314 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17315 {
17317 Oid relOid = relForm->oid;
17318
17319 /*
17320 * Do not move objects in pg_catalog as part of this, if an admin
17321 * really wishes to do so, they can issue the individual ALTER
17322 * commands directly.
17323 *
17324 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17325 * (TOAST will be moved with the main table).
17326 */
17327 if (IsCatalogNamespace(relForm->relnamespace) ||
17328 relForm->relisshared ||
17329 isAnyTempNamespace(relForm->relnamespace) ||
17330 IsToastNamespace(relForm->relnamespace))
17331 continue;
17332
17333 /* Only move the object type requested */
17334 if ((stmt->objtype == OBJECT_TABLE &&
17335 relForm->relkind != RELKIND_RELATION &&
17336 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17337 (stmt->objtype == OBJECT_INDEX &&
17338 relForm->relkind != RELKIND_INDEX &&
17339 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17340 (stmt->objtype == OBJECT_MATVIEW &&
17341 relForm->relkind != RELKIND_MATVIEW))
17342 continue;
17343
17344 /* Check if we are only moving objects owned by certain roles */
17345 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17346 continue;
17347
17348 /*
17349 * Handle permissions-checking here since we are locking the tables
17350 * and also to avoid doing a bunch of work only to fail part-way. Note
17351 * that permissions will also be checked by AlterTableInternal().
17352 *
17353 * Caller must be considered an owner on the table to move it.
17354 */
17357 NameStr(relForm->relname));
17358
17359 if (stmt->nowait &&
17361 ereport(ERROR,
17363 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17364 get_namespace_name(relForm->relnamespace),
17365 NameStr(relForm->relname))));
17366 else
17368
17369 /* Add to our list of objects to move */
17370 relations = lappend_oid(relations, relOid);
17371 }
17372
17373 table_endscan(scan);
17375
17376 if (relations == NIL)
17379 errmsg("no matching relations in tablespace \"%s\" found",
17380 orig_tablespaceoid == InvalidOid ? "(database default)" :
17382
17383 /* Everything is locked, loop through and move all of the relations. */
17384 foreach(l, relations)
17385 {
17386 List *cmds = NIL;
17388
17390 cmd->name = stmt->new_tablespacename;
17391
17392 cmds = lappend(cmds, cmd);
17393
17395 /* OID is set by AlterTableInternal */
17396 AlterTableInternal(lfirst_oid(l), cmds, false);
17398 }
17399
17400 return new_tablespaceoid;
17401}
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
@ ACLCHECK_NOT_OWNER
Definition acl.h:186
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2672
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3880
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4134
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:36
void EventTriggerAlterTableStart(const Node *parsetree)
void EventTriggerAlterTableEnd(void)
Oid MyDatabaseTableSpace
Definition globals.c:98
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition heapam.c:1435
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:2309
Oid GetUserId(void)
Definition miscinit.c:470
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:1061
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition tablecmds.c:4620
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 19194 of file tablecmds.c.

19195{
19196 Relation rel;
19197 Oid relid;
19198 Oid oldNspOid;
19199 Oid nspOid;
19200 RangeVar *newrv;
19203
19205 stmt->missing_ok ? RVR_MISSING_OK : 0,
19207 stmt);
19208
19209 if (!OidIsValid(relid))
19210 {
19212 (errmsg("relation \"%s\" does not exist, skipping",
19213 stmt->relation->relname)));
19214 return InvalidObjectAddress;
19215 }
19216
19217 rel = relation_open(relid, NoLock);
19218
19220
19221 /* If it's an owned sequence, disallow moving it by itself. */
19222 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19223 {
19224 Oid tableId;
19225 int32 colId;
19226
19227 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19229 ereport(ERROR,
19231 errmsg("cannot move an owned sequence into another schema"),
19232 errdetail("Sequence \"%s\" is linked to table \"%s\".",
19235 }
19236
19237 /* Get and lock schema OID and check its permissions. */
19238 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19240
19241 /* common checks on switching namespaces */
19243
19247
19249
19250 if (oldschema)
19252
19253 /* close rel, but keep lock until commit */
19254 relation_close(rel, NoLock);
19255
19256 return myself;
19257}
int32_t int32
Definition c.h:620
ObjectAddresses * new_object_addresses(void)
void free_object_addresses(ObjectAddresses *addrs)
int errdetail(const char *fmt,...) pg_attribute_printf(1
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:1032
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 19265 of file tablecmds.c.

19267{
19269
19270 Assert(objsMoved != NULL);
19271
19272 /* OK, modify the pg_class row and pg_depend entry */
19274
19276 nspOid, true, objsMoved);
19277
19278 /* Fix the table's row type too, if it has one */
19279 if (OidIsValid(rel->rd_rel->reltype))
19281 false, /* isImplicitArray */
19282 false, /* ignoreDependent */
19283 false, /* errorOnTableType */
19284 objsMoved);
19285
19286 /* Fix other dependent stuff */
19291 false, objsMoved);
19292
19294}
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:4206

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

◆ applyPartitionIndexExtDeps()

static void applyPartitionIndexExtDeps ( Oid  newPartOid,
List extDepState 
)
static

Definition at line 23175 of file tablecmds.c.

23176{
23177 Relation partRel;
23178 List *indexList;
23179
23180 if (extDepState == NIL)
23181 return;
23182
23183 /*
23184 * Use NoLock since the caller already holds AccessExclusiveLock on the
23185 * new partition.
23186 */
23187 partRel = table_open(newPartOid, NoLock);
23189
23190 foreach_oid(indexOid, indexList)
23191 {
23193
23194 if (!get_rel_relispartition(indexOid))
23195 continue;
23196
23197 parentIdxOid = get_partition_parent(indexOid, true);
23199 continue;
23200
23202 {
23204
23205 if (entry->parentIndexOid > parentIdxOid)
23206 break;
23207 if (entry->parentIndexOid < parentIdxOid)
23208 continue;
23209
23211
23212 foreach_oid(extOid, entry->extensionOids)
23213 {
23215
23219 }
23220 break;
23221 }
23222 }
23223
23225 table_close(partRel, NoLock);
23226}
@ DEPENDENCY_AUTO_EXTENSION
Definition dependency.h:39
bool get_rel_relispartition(Oid relid)
Definition lsyscache.c:2333
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition partition.c:53
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
#define foreach_oid(var, lst)
Definition pg_list.h:503

References DEPENDENCY_AUTO_EXTENSION, fb(), foreach_oid, foreach_ptr, get_partition_parent(), get_rel_relispartition(), list_free(), NIL, NoLock, ObjectAddressSet, OidIsValid, recordDependencyOn(), RelationGetIndexList(), table_close(), and table_open().

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

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

9993{
9994 List *newcons;
9995 ListCell *lcon;
9996 List *children;
9997 ListCell *child;
9999
10000 /* Guard against stack overflow due to overly deep inheritance tree. */
10002
10003 /* At top level, permission check was done in ATPrepCmd, else do it */
10004 if (recursing)
10007
10008 /*
10009 * Call AddRelationNewConstraints to do the work, making sure it works on
10010 * a copy of the Constraint so transformExpr can't modify the original. It
10011 * returns a list of cooked constraints.
10012 *
10013 * If the constraint ends up getting merged with a pre-existing one, it's
10014 * omitted from the returned list, which is what we want: we do not need
10015 * to do any validation work. That can only happen at child tables,
10016 * though, since we disallow merging at the top level.
10017 */
10019 list_make1(copyObject(constr)),
10020 recursing || is_readd, /* allow_merge */
10021 !recursing, /* is_local */
10022 is_readd, /* is_internal */
10023 NULL); /* queryString not available
10024 * here */
10025
10026 /* we don't expect more than one constraint here */
10027 Assert(list_length(newcons) <= 1);
10028
10029 /* Add each to-be-validated constraint to Phase 3's queue */
10030 foreach(lcon, newcons)
10031 {
10033
10034 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
10035 {
10037
10039 newcon->name = ccon->name;
10040 newcon->contype = ccon->contype;
10041 newcon->qual = ccon->expr;
10042
10043 tab->constraints = lappend(tab->constraints, newcon);
10044 }
10045
10046 /* Save the actually assigned name if it was defaulted */
10047 if (constr->conname == NULL)
10048 constr->conname = ccon->name;
10049
10050 /*
10051 * If adding a valid not-null constraint, set the pg_attribute flag
10052 * and tell phase 3 to verify existing rows, if needed. For an
10053 * invalid constraint, just set attnotnull, without queueing
10054 * verification.
10055 */
10056 if (constr->contype == CONSTR_NOTNULL)
10057 set_attnotnull(wqueue, rel, ccon->attnum,
10058 !constr->skip_validation,
10059 !constr->skip_validation);
10060
10061 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
10062 }
10063
10064 /* At this point we must have a locked-down name to use */
10065 Assert(newcons == NIL || constr->conname != NULL);
10066
10067 /* Advance command counter in case same table is visited multiple times */
10069
10070 /*
10071 * If the constraint got merged with an existing constraint, we're done.
10072 * We mustn't recurse to child tables in this case, because they've
10073 * already got the constraint, and visiting them again would lead to an
10074 * incorrect value for coninhcount.
10075 */
10076 if (newcons == NIL)
10077 return address;
10078
10079 /*
10080 * If adding a NO INHERIT constraint, no need to find our children.
10081 */
10082 if (constr->is_no_inherit)
10083 return address;
10084
10085 /*
10086 * Propagate to children as appropriate. Unlike most other ALTER
10087 * routines, we have to do this one level of recursion at a time; we can't
10088 * use find_all_inheritors to do it in one pass.
10089 */
10090 children =
10092
10093 /*
10094 * Check if ONLY was specified with ALTER TABLE. If so, allow the
10095 * constraint creation only if there are no children currently. Error out
10096 * otherwise.
10097 */
10098 if (!recurse && children != NIL)
10099 ereport(ERROR,
10101 errmsg("constraint must be added to child tables too")));
10102
10103 /*
10104 * Recurse to create the constraint on each child.
10105 */
10106 foreach(child, children)
10107 {
10108 Oid childrelid = lfirst_oid(child);
10111
10112 /* find_inheritance_children already got lock */
10115
10116 /* Find or create work queue entry for this table */
10118
10119 /* Recurse to this child */
10121 constr, recurse, true, is_readd, lockmode);
10122
10124 }
10125
10126 return address;
10127}
List * AddRelationNewConstraints(Relation rel, List *newColDefaults, List *newConstraints, bool allow_merge, bool is_local, bool is_internal, const char *queryString)
Definition heap.c:2405
@ CONSTR_NOTNULL
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition pg_inherits.c:59
static int list_length(const List *l)
Definition pg_list.h:152
#define list_make1(x1)
Definition pg_list.h:244
void check_stack_depth(void)
Definition stack_depth.c:96
ConstrType contype
bool is_no_inherit
bool skip_validation
char * conname
#define ATT_TABLE
Definition tablecmds.c:338
static ObjectAddress ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
Definition tablecmds.c:9990
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition tablecmds.c:6829
#define ATT_FOREIGN_TABLE
Definition tablecmds.c:343
#define ATT_PARTITIONED_TABLE
Definition tablecmds.c:346
static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool is_valid, bool queue_validation)
Definition tablecmds.c:7930

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

10148{
10161 bool with_period;
10163 int i;
10164 int numfks,
10165 numpks,
10167 Oid indexOid;
10168 bool old_check_ok;
10169 ObjectAddress address;
10171
10172 /*
10173 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10174 * delete rows out from under us.
10175 */
10176 if (OidIsValid(fkconstraint->old_pktable_oid))
10178 else
10180
10181 /*
10182 * Validity checks (permission checks wait till we have the column
10183 * numbers)
10184 */
10185 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10186 ereport(ERROR,
10188 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10191
10192 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10193 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10194 ereport(ERROR,
10196 errmsg("referenced relation \"%s\" is not a table",
10198
10200 ereport(ERROR,
10202 errmsg("permission denied: \"%s\" is a system catalog",
10204
10205 /*
10206 * References from permanent or unlogged tables to temp tables, and from
10207 * permanent tables to unlogged tables, are disallowed because the
10208 * referenced data can vanish out from under us. References from temp
10209 * tables to any other table type are also disallowed, because other
10210 * backends might need to run the RI triggers on the perm table, but they
10211 * can't reliably see tuples in the local buffers of other backends.
10212 */
10213 switch (rel->rd_rel->relpersistence)
10214 {
10217 ereport(ERROR,
10219 errmsg("constraints on permanent tables may reference only permanent tables")));
10220 break;
10223 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10224 ereport(ERROR,
10226 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10227 break;
10229 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10230 ereport(ERROR,
10232 errmsg("constraints on temporary tables may reference only temporary tables")));
10233 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10234 ereport(ERROR,
10236 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10237 break;
10238 }
10239
10240 /*
10241 * Look up the referencing attributes to make sure they exist, and record
10242 * their attnums and type and collation OIDs.
10243 */
10245 fkconstraint->fk_attrs,
10247 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10248 if (with_period && !fkconstraint->fk_with_period)
10249 ereport(ERROR,
10251 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10252
10254 fkconstraint->fk_del_set_cols,
10259 fkconstraint->fk_del_set_cols);
10260
10261 /*
10262 * If the attribute list for the referenced table was omitted, lookup the
10263 * definition of the primary key and use it. Otherwise, validate the
10264 * supplied attribute list. In either case, discover the index OID and
10265 * index opclasses, and the attnums and type and collation OIDs of the
10266 * attributes.
10267 */
10268 if (fkconstraint->pk_attrs == NIL)
10269 {
10271 &fkconstraint->pk_attrs,
10274
10275 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10276 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10277 ereport(ERROR,
10279 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10280 }
10281 else
10282 {
10284 fkconstraint->pk_attrs,
10286
10287 /* Since we got pk_attrs, one should be a period. */
10288 if (with_period && !fkconstraint->pk_with_period)
10289 ereport(ERROR,
10291 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10292
10293 /* Look for an index matching the column list */
10296 }
10297
10298 /*
10299 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10300 * must use PERIOD.
10301 */
10303 ereport(ERROR,
10305 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10306
10307 /*
10308 * Now we can check permissions.
10309 */
10311
10312 /*
10313 * Check some things for generated columns.
10314 */
10315 for (i = 0; i < numfks; i++)
10316 {
10317 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10318
10319 if (attgenerated)
10320 {
10321 /*
10322 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10323 */
10324 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10325 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10326 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10327 ereport(ERROR,
10329 errmsg("invalid %s action for foreign key constraint containing generated column",
10330 "ON UPDATE")));
10331 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10332 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10333 ereport(ERROR,
10335 errmsg("invalid %s action for foreign key constraint containing generated column",
10336 "ON DELETE")));
10337 }
10338
10339 /*
10340 * FKs on virtual columns are not supported. This would require
10341 * various additional support in ri_triggers.c, including special
10342 * handling in ri_NullCheck(), ri_KeysEqual(),
10343 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10344 * as NULL there). Also not really practical as long as you can't
10345 * index virtual columns.
10346 */
10347 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10348 ereport(ERROR,
10350 errmsg("foreign key constraints on virtual generated columns are not supported")));
10351 }
10352
10353 /*
10354 * Some actions are currently unsupported for foreign keys using PERIOD.
10355 */
10356 if (fkconstraint->fk_with_period)
10357 {
10358 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10359 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10360 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10361 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10362 ereport(ERROR,
10364 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10365 "ON UPDATE"));
10366
10367 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10368 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10369 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10370 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10371 ereport(ERROR,
10373 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10374 "ON DELETE"));
10375 }
10376
10377 /*
10378 * Look up the equality operators to use in the constraint.
10379 *
10380 * Note that we have to be careful about the difference between the actual
10381 * PK column type and the opclass' declared input type, which might be
10382 * only binary-compatible with it. The declared opcintype is the right
10383 * thing to probe pg_amop with.
10384 */
10385 if (numfks != numpks)
10386 ereport(ERROR,
10388 errmsg("number of referencing and referenced columns for foreign key disagree")));
10389
10390 /*
10391 * On the strength of a previous constraint, we might avoid scanning
10392 * tables to validate this one. See below.
10393 */
10394 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10395 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10396
10397 for (i = 0; i < numpks; i++)
10398 {
10399 Oid pktype = pktypoid[i];
10400 Oid fktype = fktypoid[i];
10401 Oid fktyped;
10402 Oid pkcoll = pkcolloid[i];
10403 Oid fkcoll = fkcolloid[i];
10406 Oid amid;
10407 Oid opfamily;
10408 Oid opcintype;
10409 bool for_overlaps;
10410 CompareType cmptype;
10411 Oid pfeqop;
10412 Oid ppeqop;
10413 Oid ffeqop;
10416
10417 /* We need several fields out of the pg_opclass entry */
10420 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10422 amid = cla_tup->opcmethod;
10423 opfamily = cla_tup->opcfamily;
10424 opcintype = cla_tup->opcintype;
10426
10427 /*
10428 * Get strategy number from index AM.
10429 *
10430 * For a normal foreign-key constraint, this should not fail, since we
10431 * already checked that the index is unique and should therefore have
10432 * appropriate equal operators. For a period foreign key, this could
10433 * fail if we selected a non-matching exclusion constraint earlier.
10434 * (XXX Maybe we should do these lookups earlier so we don't end up
10435 * doing that.)
10436 */
10437 for_overlaps = with_period && i == numpks - 1;
10439 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10441 ereport(ERROR,
10444 ? errmsg("could not identify an overlaps operator for foreign key")
10445 : errmsg("could not identify an equality operator for foreign key"),
10446 errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10447 cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10448
10449 /*
10450 * There had better be a primary equality operator for the index.
10451 * We'll use it for PK = PK comparisons.
10452 */
10453 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10454 eqstrategy);
10455
10456 if (!OidIsValid(ppeqop))
10457 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10458 eqstrategy, opcintype, opcintype, opfamily);
10459
10460 /*
10461 * Are there equality operators that take exactly the FK type? Assume
10462 * we should look through any domain here.
10463 */
10465
10466 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10467 eqstrategy);
10468 if (OidIsValid(pfeqop))
10469 {
10472 eqstrategy);
10473 }
10474 else
10475 {
10476 /* keep compiler quiet */
10479 }
10480
10481 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10482 {
10483 /*
10484 * Otherwise, look for an implicit cast from the FK type to the
10485 * opcintype, and if found, use the primary equality operator.
10486 * This is a bit tricky because opcintype might be a polymorphic
10487 * type such as ANYARRAY or ANYENUM; so what we have to test is
10488 * whether the two actual column types can be concurrently cast to
10489 * that type. (Otherwise, we'd fail to reject combinations such
10490 * as int[] and point[].)
10491 */
10492 Oid input_typeids[2];
10494
10495 input_typeids[0] = pktype;
10496 input_typeids[1] = fktype;
10497 target_typeids[0] = opcintype;
10498 target_typeids[1] = opcintype;
10501 {
10502 pfeqop = ffeqop = ppeqop;
10503 pfeqop_right = opcintype;
10504 }
10505 }
10506
10507 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10508 ereport(ERROR,
10510 errmsg("foreign key constraint \"%s\" cannot be implemented",
10511 fkconstraint->conname),
10512 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10513 "are of incompatible types: %s and %s.",
10514 strVal(list_nth(fkconstraint->fk_attrs, i)),
10515 strVal(list_nth(fkconstraint->pk_attrs, i)),
10518
10519 /*
10520 * This shouldn't be possible, but better check to make sure we have a
10521 * consistent state for the check below.
10522 */
10524 elog(ERROR, "key columns are not both collatable");
10525
10527 {
10528 bool pkcolldet;
10529 bool fkcolldet;
10530
10533
10534 /*
10535 * SQL requires that both collations are the same. This is
10536 * because we need a consistent notion of equality on both
10537 * columns. We relax this by allowing different collations if
10538 * they are both deterministic. (This is also for backward
10539 * compatibility, because PostgreSQL has always allowed this.)
10540 */
10541 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10542 ereport(ERROR,
10544 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10545 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10546 "have incompatible collations: \"%s\" and \"%s\". "
10547 "If either collation is nondeterministic, then both collations have to be the same.",
10548 strVal(list_nth(fkconstraint->fk_attrs, i)),
10549 strVal(list_nth(fkconstraint->pk_attrs, i)),
10552 }
10553
10554 if (old_check_ok)
10555 {
10556 /*
10557 * When a pfeqop changes, revalidate the constraint. We could
10558 * permit intra-opfamily changes, but that adds subtle complexity
10559 * without any concrete benefit for core types. We need not
10560 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10561 */
10563 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10565 }
10566 if (old_check_ok)
10567 {
10577 fkattnum[i] - 1);
10578
10579 /*
10580 * Identify coercion pathways from each of the old and new FK-side
10581 * column types to the right (foreign) operand type of the pfeqop.
10582 * We may assume that pg_constraint.conkey is not changing.
10583 */
10584 old_fktype = attr->atttypid;
10587 &old_castfunc);
10589 &new_castfunc);
10590
10591 old_fkcoll = attr->attcollation;
10593
10594 /*
10595 * Upon a change to the cast from the FK column to its pfeqop
10596 * operand, revalidate the constraint. For this evaluation, a
10597 * binary coercion cast is equivalent to no cast at all. While
10598 * type implementors should design implicit casts with an eye
10599 * toward consistency of operations like equality, we cannot
10600 * assume here that they have done so.
10601 *
10602 * A function with a polymorphic argument could change behavior
10603 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10604 * when the cast destination is polymorphic, we only avoid
10605 * revalidation if the input type has not changed at all. Given
10606 * just the core data types and operator classes, this requirement
10607 * prevents no would-be optimizations.
10608 *
10609 * If the cast converts from a base type to a domain thereon, then
10610 * that domain type must be the opcintype of the unique index.
10611 * Necessarily, the primary key column must then be of the domain
10612 * type. Since the constraint was previously valid, all values on
10613 * the foreign side necessarily exist on the primary side and in
10614 * turn conform to the domain. Consequently, we need not treat
10615 * domains specially here.
10616 *
10617 * If the collation changes, revalidation is required, unless both
10618 * collations are deterministic, because those share the same
10619 * notion of equality (because texteq reduces to bitwise
10620 * equality).
10621 *
10622 * We need not directly consider the PK type. It's necessarily
10623 * binary coercible to the opcintype of the unique index column,
10624 * and ri_triggers.c will only deal with PK datums in terms of
10625 * that opcintype. Changing the opcintype also changes pfeqop.
10626 */
10630 new_fktype == old_fktype) &&
10631 (new_fkcoll == old_fkcoll ||
10633 }
10634
10638 }
10639
10640 /*
10641 * For FKs with PERIOD we need additional operators to check whether the
10642 * referencing row's range is contained by the aggregated ranges of the
10643 * referenced row(s). For rangetypes and multirangetypes this is
10644 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10645 * support for now. FKs will look these up at "runtime", but we should
10646 * make sure the lookup works here, even if we don't use the values.
10647 */
10648 if (with_period)
10649 {
10653
10656 }
10657
10658 /* First, create the constraint catalog entry itself. */
10660 fkconstraint->conname, fkconstraint, rel, pkrel,
10661 indexOid,
10662 InvalidOid, /* no parent constraint */
10663 numfks,
10664 pkattnum,
10665 fkattnum,
10671 false,
10672 with_period);
10673
10674 /* Next process the action triggers at the referenced side and recurse */
10676 indexOid,
10677 address.objectId,
10678 numfks,
10679 pkattnum,
10680 fkattnum,
10688 with_period);
10689
10690 /* Lastly create the check triggers at the referencing side and recurse */
10692 indexOid,
10693 address.objectId,
10694 numfks,
10695 pkattnum,
10696 fkattnum,
10703 lockmode,
10705 with_period);
10706
10707 /*
10708 * Done. Close pk table, but keep lock until we've committed.
10709 */
10711
10712 return address;
10713}
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:132
#define false
char * get_collation_name(Oid colloid)
Definition lsyscache.c:1253
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition lsyscache.c:170
bool get_collation_isdeterministic(Oid colloid)
Definition lsyscache.c:1272
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition lsyscache.c:1545
Oid getBaseType(Oid typid)
Definition lsyscache.c:2829
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:331
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:375
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:628
#define InvalidStrategy
Definition stratnum.h:24
TupleDesc oldDesc
Definition tablecmds.c:177
bool rd_islocaltemp
Definition rel.h:61
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
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:178
#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 6951 of file tablecmds.c.

6952{
6953 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6954 {
6955 List *inh;
6956 ListCell *cell;
6957
6958 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6959 /* first element is the parent rel; must ignore it */
6960 for_each_from(cell, inh, 1)
6961 {
6963
6964 /* find_all_inheritors already got lock */
6968 }
6969 list_free(inh);
6970 }
6971}
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
#define for_each_from(cell, lst, N)
Definition pg_list.h:446

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

14936{
14937 Assert(expr != NULL);
14938
14939 for (;;)
14940 {
14941 /* only one varno, so no need to check that */
14942 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14943 return false;
14944 else if (IsA(expr, RelabelType))
14945 expr = (Node *) ((RelabelType *) expr)->arg;
14946 else if (IsA(expr, CoerceToDomain))
14947 {
14948 CoerceToDomain *d = (CoerceToDomain *) expr;
14949
14951 return true;
14952 expr = (Node *) d->arg;
14953 }
14954 else if (IsA(expr, FuncExpr))
14955 {
14956 FuncExpr *f = (FuncExpr *) expr;
14957
14958 switch (f->funcid)
14959 {
14963 return true;
14964 else
14965 expr = linitial(f->args);
14966 break;
14967 default:
14968 return true;
14969 }
14970 }
14971 else
14972 return true;
14973 }
14974}
bool TimestampTimestampTzRequiresRewrite(void)
Definition timestamp.c:6456
Datum arg
Definition elog.c:1323
#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, bool *has_volatile)
Definition typcache.c:1488

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

4935{
4936 List *wqueue = NIL;
4937 ListCell *lcmd;
4938
4939 /* Phase 1: preliminary examination of commands, create work queue */
4940 foreach(lcmd, cmds)
4941 {
4943
4944 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4945 }
4946
4947 /* Close the relation, but keep lock until commit */
4948 relation_close(rel, NoLock);
4949
4950 /* Phase 2: update system catalogs */
4951 ATRewriteCatalogs(&wqueue, lockmode, context);
4952
4953 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4954 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4955}
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:5370
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:4967
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:5922

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

22270{
22271 List *constraints;
22272 ListCell *cell;
22273
22274 constraints = GetParentedForeignKeyRefs(partition);
22275
22276 foreach(cell, constraints)
22277 {
22278 Oid constrOid = lfirst_oid(cell);
22279 HeapTuple tuple;
22281 Relation rel;
22282 Trigger trig = {0};
22283
22285 if (!HeapTupleIsValid(tuple))
22286 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22288
22289 Assert(OidIsValid(constrForm->conparentid));
22291
22292 /* prevent data changes into the referencing table until commit */
22293 rel = table_open(constrForm->conrelid, ShareLock);
22294
22295 trig.tgoid = InvalidOid;
22296 trig.tgname = NameStr(constrForm->conname);
22297 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
22298 trig.tgisinternal = true;
22299 trig.tgconstrrelid = RelationGetRelid(partition);
22300 trig.tgconstrindid = constrForm->conindid;
22301 trig.tgconstraint = constrForm->oid;
22302 trig.tgdeferrable = false;
22303 trig.tginitdeferred = false;
22304 /* we needn't fill in remaining fields */
22305
22307
22308 ReleaseSysCache(tuple);
22309
22310 table_close(rel, NoLock);
22311 }
22312}
#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 19707 of file tablecmds.c.

19709{
19711
19712 foreach(cur_item, on_commits)
19713 {
19715
19716 if (!isCommit && oc->creating_subid == mySubid)
19717 {
19718 /* cur_item must be removed */
19720 pfree(oc);
19721 }
19722 else
19723 {
19724 /* cur_item must be preserved */
19725 if (oc->creating_subid == mySubid)
19726 oc->creating_subid = parentSubid;
19727 if (oc->deleting_subid == mySubid)
19728 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19729 }
19730 }
19731}
#define InvalidSubTransactionId
Definition c.h:742
#define foreach_delete_current(lst, var_or_cell)
Definition pg_list.h:423
static List * on_commits
Definition tablecmds.c:135

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

19676{
19678
19679 foreach(cur_item, on_commits)
19680 {
19682
19683 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19684 oc->creating_subid != InvalidSubTransactionId)
19685 {
19686 /* cur_item must be removed */
19688 pfree(oc);
19689 }
19690 else
19691 {
19692 /* cur_item must be preserved */
19693 oc->creating_subid = InvalidSubTransactionId;
19694 oc->deleting_subid = InvalidSubTransactionId;
19695 }
19696 }
19697}

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

7311{
7313 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7314 bool if_not_exists = (*cmd)->missing_ok;
7316 attrdesc;
7320 int newattnum;
7321 char relkind;
7322 Expr *defval;
7323 List *children;
7324 ListCell *child;
7326 ObjectAddress address;
7327 TupleDesc tupdesc;
7328
7329 /* since this function recurses, it could be driven to stack overflow */
7331
7332 /* At top level, permission check was done in ATPrepCmd, else do it */
7333 if (recursing)
7334 ATSimplePermissions((*cmd)->subtype, rel,
7336
7337 if (rel->rd_rel->relispartition && !recursing)
7338 ereport(ERROR,
7340 errmsg("cannot add column to a partition")));
7341
7343
7344 /*
7345 * Are we adding the column to a recursion child? If so, check whether to
7346 * merge with an existing definition for the column. If we do merge, we
7347 * must not recurse. Children will already have the column, and recursing
7348 * into them would mess up attinhcount.
7349 */
7350 if (colDef->inhcount > 0)
7351 {
7352 HeapTuple tuple;
7353
7354 /* Does child already have a column by this name? */
7355 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7356 if (HeapTupleIsValid(tuple))
7357 {
7359 Oid ctypeId;
7360 int32 ctypmod;
7361 Oid ccollid;
7362
7363 /* Child column must match on type, typmod, and collation */
7365 if (ctypeId != childatt->atttypid ||
7366 ctypmod != childatt->atttypmod)
7367 ereport(ERROR,
7369 errmsg("child table \"%s\" has different type for column \"%s\"",
7370 RelationGetRelationName(rel), colDef->colname)));
7372 if (ccollid != childatt->attcollation)
7373 ereport(ERROR,
7375 errmsg("child table \"%s\" has different collation for column \"%s\"",
7376 RelationGetRelationName(rel), colDef->colname),
7377 errdetail("\"%s\" versus \"%s\"",
7379 get_collation_name(childatt->attcollation))));
7380
7381 /* Bump the existing child att's inhcount */
7382 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7383 &childatt->attinhcount))
7384 ereport(ERROR,
7386 errmsg("too many inheritance parents"));
7387 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7388
7389 heap_freetuple(tuple);
7390
7391 /* Inform the user about the merge */
7393 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7394 colDef->colname, RelationGetRelationName(rel))));
7395
7397
7398 /* Make the child column change visible */
7400
7401 return InvalidObjectAddress;
7402 }
7403 }
7404
7405 /* skip if the name already exists and if_not_exists is true */
7406 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7407 {
7409 return InvalidObjectAddress;
7410 }
7411
7412 /*
7413 * Okay, we need to add the column, so go ahead and do parse
7414 * transformation. This can result in queueing up, or even immediately
7415 * executing, subsidiary operations (such as creation of unique indexes);
7416 * so we mustn't do it until we have made the if_not_exists check.
7417 *
7418 * When recursing, the command was already transformed and we needn't do
7419 * so again. Also, if context isn't given we can't transform. (That
7420 * currently happens only for AT_AddColumnToView; we expect that view.c
7421 * passed us a ColumnDef that doesn't need work.)
7422 */
7423 if (context != NULL && !recursing)
7424 {
7425 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7426 cur_pass, context);
7427 Assert(*cmd != NULL);
7428 colDef = castNode(ColumnDef, (*cmd)->def);
7429 }
7430
7431 /*
7432 * Regular inheritance children are independent enough not to inherit the
7433 * identity column from parent hence cannot recursively add identity
7434 * column if the table has inheritance children.
7435 *
7436 * Partitions, on the other hand, are integral part of a partitioned table
7437 * and inherit identity column. Hence propagate identity column down the
7438 * partition hierarchy.
7439 */
7440 if (colDef->identity &&
7441 recurse &&
7442 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7444 ereport(ERROR,
7446 errmsg("cannot recursively add identity column to table that has child tables")));
7447
7449
7452 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7454 relkind = relform->relkind;
7455
7456 /* Determine the new attribute's number */
7457 newattnum = relform->relnatts + 1;
7459 ereport(ERROR,
7461 errmsg("tables can have at most %d columns",
7463
7464 /*
7465 * Construct new attribute's pg_attribute entry.
7466 */
7468
7469 attribute = TupleDescAttr(tupdesc, 0);
7470
7471 /* Fix up attribute number */
7472 attribute->attnum = newattnum;
7473
7474 /* make sure datatype is legal for a column */
7475 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7476 list_make1_oid(rel->rd_rel->reltype),
7478
7480
7482
7483 /*
7484 * Update pg_class tuple as appropriate
7485 */
7486 relform->relnatts = newattnum;
7487
7489
7491
7492 /* Post creation hook for new attribute */
7494
7496
7497 /* Make the attribute's catalog entry visible */
7499
7500 /*
7501 * Store the DEFAULT, if any, in the catalogs
7502 */
7503 if (colDef->raw_default)
7504 {
7506
7508 rawEnt->attnum = attribute->attnum;
7509 rawEnt->raw_default = copyObject(colDef->raw_default);
7510 rawEnt->generated = colDef->generated;
7511
7512 /*
7513 * This function is intended for CREATE TABLE, so it processes a
7514 * _list_ of defaults, but we just do one.
7515 */
7517 false, true, false, NULL);
7518
7519 /* Make the additional catalog changes visible */
7521 }
7522
7523 /*
7524 * Tell Phase 3 to fill in the default expression, if there is one.
7525 *
7526 * If there is no default, Phase 3 doesn't have to do anything, because
7527 * that effectively means that the default is NULL. The heap tuple access
7528 * routines always check for attnum > # of attributes in tuple, and return
7529 * NULL if so, so without any modification of the tuple data we will get
7530 * the effect of NULL values in the new column.
7531 *
7532 * An exception occurs when the new column is of a domain type: the domain
7533 * might have a not-null constraint, or a check constraint that indirectly
7534 * rejects nulls. If there are any domain constraints then we construct
7535 * an explicit NULL default value that will be passed through
7536 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7537 * rewriting the table which we really wouldn't have to do; but we do it
7538 * to preserve the historical behavior that such a failure will be raised
7539 * only if the table currently contains some rows.)
7540 *
7541 * Note: we use build_column_default, and not just the cooked default
7542 * returned by AddRelationNewConstraints, so that the right thing happens
7543 * when a datatype's default applies.
7544 *
7545 * Note: it might seem that this should happen at the end of Phase 2, so
7546 * that the effects of subsequent subcommands can be taken into account.
7547 * It's intentional that we do it now, though. The new column should be
7548 * filled according to what is said in the ADD COLUMN subcommand, so that
7549 * the effects are the same as if this subcommand had been run by itself
7550 * and the later subcommands had been issued in new ALTER TABLE commands.
7551 *
7552 * We can skip this entirely for relations without storage, since Phase 3
7553 * is certainly not going to touch them.
7554 */
7555 if (RELKIND_HAS_STORAGE(relkind))
7556 {
7558 bool has_missing = false;
7559
7560 /*
7561 * For an identity column, we can't use build_column_default(),
7562 * because the sequence ownership isn't set yet. So do it manually.
7563 */
7564 if (colDef->identity)
7565 {
7567
7568 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7569 nve->typeId = attribute->atttypid;
7570
7571 defval = (Expr *) nve;
7572 }
7573 else
7574 defval = (Expr *) build_column_default(rel, attribute->attnum);
7575
7576 /* Build CoerceToDomain(NULL) expression if needed */
7578 if (!defval && has_domain_constraints)
7579 {
7583
7584 baseTypeMod = attribute->atttypmod;
7588 defval = (Expr *) coerce_to_target_type(NULL,
7589 (Node *) defval,
7590 baseTypeId,
7591 attribute->atttypid,
7592 attribute->atttypmod,
7595 -1);
7596 if (defval == NULL) /* should not happen */
7597 elog(ERROR, "failed to coerce base type to domain");
7598 }
7599
7600 if (defval)
7601 {
7603
7604 /* Prepare defval for execution, either here or in Phase 3 */
7605 defval = expression_planner(defval);
7606
7607 /* Add the new default to the newvals list */
7609 newval->attnum = attribute->attnum;
7610 newval->expr = defval;
7611 newval->is_generated = (colDef->generated != '\0');
7612
7613 tab->newvals = lappend(tab->newvals, newval);
7614
7615 /*
7616 * Attempt to skip a complete table rewrite by storing the
7617 * specified DEFAULT value outside of the heap. This is only
7618 * allowed for plain relations and non-generated columns, and the
7619 * default expression can't be volatile (stable is OK). Note that
7620 * contain_volatile_functions deems CoerceToDomain immutable, but
7621 * here we consider that coercion to a domain with constraints is
7622 * volatile; else it might fail even when the table is empty.
7623 */
7624 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7625 !colDef->generated &&
7627 !contain_volatile_functions((Node *) defval))
7628 {
7629 EState *estate;
7632 bool missingIsNull;
7633
7634 /* Evaluate the default expression */
7635 estate = CreateExecutorState();
7636 exprState = ExecPrepareExpr(defval, estate);
7638 GetPerTupleExprContext(estate),
7639 &missingIsNull);
7640 /* If it turns out NULL, nothing to do; else store it */
7641 if (!missingIsNull)
7642 {
7644 /* Make the additional catalog change visible */
7646 has_missing = true;
7647 }
7648 FreeExecutorState(estate);
7649 }
7650 else
7651 {
7652 /*
7653 * Failed to use missing mode. We have to do a table rewrite
7654 * to install the value --- unless it's a virtual generated
7655 * column.
7656 */
7657 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7659 }
7660 }
7661
7662 if (!has_missing)
7663 {
7664 /*
7665 * If the new column is NOT NULL, and there is no missing value,
7666 * tell Phase 3 it needs to check for NULLs.
7667 */
7668 tab->verify_new_notnull |= colDef->is_not_null;
7669 }
7670 }
7671
7672 /*
7673 * Add needed dependency entries for the new column.
7674 */
7677
7678 /*
7679 * Propagate to children as appropriate. Unlike most other ALTER
7680 * routines, we have to do this one level of recursion at a time; we can't
7681 * use find_all_inheritors to do it in one pass.
7682 */
7683 children =
7685
7686 /*
7687 * If we are told not to recurse, there had better not be any child
7688 * tables; else the addition would put them out of step.
7689 */
7690 if (children && !recurse)
7691 ereport(ERROR,
7693 errmsg("column must be added to child tables too")));
7694
7695 /* Children should see column as singly inherited */
7696 if (!recursing)
7697 {
7698 childcmd = copyObject(*cmd);
7700 colDef->inhcount = 1;
7701 colDef->is_local = false;
7702 }
7703 else
7704 childcmd = *cmd; /* no need to copy again */
7705
7706 foreach(child, children)
7707 {
7708 Oid childrelid = lfirst_oid(child);
7711
7712 /* find_inheritance_children already got lock */
7715
7716 /* Find or create work queue entry for this table */
7718
7719 /* Recurse to child; return value is ignored */
7721 &childcmd, recurse, true,
7722 lockmode, cur_pass, context);
7723
7725 }
7726
7728 return address;
7729}
bool contain_volatile_functions(Node *clause)
Definition clauses.c:551
#define AT_REWRITE_DEFAULT_VAL
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition execExpr.c:765
void FreeExecutorState(EState *estate)
Definition execUtils.c:197
EState * CreateExecutorState(void)
Definition execUtils.c:90
#define GetPerTupleExprContext(estate)
Definition executor.h:665
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:401
#define palloc_object(type)
Definition fe_memutils.h:89
#define newval
void CheckAttributeType(const char *attname, Oid atttypid, Oid attcollation, List *containing_rowtypes, int flags)
Definition heap.c:549
void InsertPgAttributeTuples(Relation pg_attribute_rel, TupleDesc tupdesc, Oid new_rel_oid, const FormExtraData_pg_attribute tupdesc_extra[], CatalogIndexState indstate)
Definition heap.c:732
void StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
Definition heap.c:2050
#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:3364
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition lsyscache.c:2846
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:274
Expr * expression_planner(Expr *expr)
Definition planner.c:7081
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:194
ItemPointerData t_self
Definition htup.h:65
HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname)
Definition syscache.c:499
#define SearchSysCacheCopy1(cacheId, key1)
Definition syscache.h:91
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition tablecmds.c:7789
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition tablecmds.c:7807
TupleDesc BuildDescForRelation(const List *columns)
Definition tablecmds.c:1429
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition tablecmds.c:5795
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition tablecmds.c:7736
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:7307

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

9877{
9879
9881
9882 /*
9883 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9884 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9885 * parse_utilcmd.c).
9886 */
9887 switch (newConstraint->contype)
9888 {
9889 case CONSTR_CHECK:
9890 case CONSTR_NOTNULL:
9891 address =
9893 newConstraint, recurse, false, is_readd,
9894 lockmode);
9895 break;
9896
9897 case CONSTR_FOREIGN:
9898
9899 /*
9900 * Assign or validate constraint name
9901 */
9902 if (newConstraint->conname)
9903 {
9905 RelationGetRelid(rel),
9906 newConstraint->conname))
9907 ereport(ERROR,
9909 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9910 newConstraint->conname,
9912 }
9913 else
9914 newConstraint->conname =
9917 "fkey",
9919 NIL);
9920
9921 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9923 recurse, false,
9924 lockmode);
9925 break;
9926
9927 default:
9928 elog(ERROR, "unrecognized constraint type: %d",
9929 (int) newConstraint->contype);
9930 }
9931
9932 return address;
9933}
@ CONSTR_CHECK
#define ERRCODE_DUPLICATE_OBJECT
Definition streamutil.c:30
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition tablecmds.c:9948
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 8330 of file tablecmds.c.

8332{
8334 HeapTuple tuple;
8337 ObjectAddress address;
8339 bool ispartitioned;
8340
8341 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8342 if (ispartitioned && !recurse)
8343 ereport(ERROR,
8345 errmsg("cannot add identity to a column of only the partitioned table"),
8346 errhint("Do not specify the ONLY keyword.")));
8347
8348 if (rel->rd_rel->relispartition && !recursing)
8349 ereport(ERROR,
8351 errmsg("cannot add identity to a column of a partition"));
8352
8354
8356 if (!HeapTupleIsValid(tuple))
8357 ereport(ERROR,
8359 errmsg("column \"%s\" of relation \"%s\" does not exist",
8362 attnum = attTup->attnum;
8363
8364 /* Can't alter a system attribute */
8365 if (attnum <= 0)
8366 ereport(ERROR,
8368 errmsg("cannot alter system column \"%s\"",
8369 colName)));
8370
8371 /*
8372 * Creating a column as identity implies NOT NULL, so adding the identity
8373 * to an existing column that is not NOT NULL would create a state that
8374 * cannot be reproduced without contortions.
8375 */
8376 if (!attTup->attnotnull)
8377 ereport(ERROR,
8379 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8381
8382 /*
8383 * On the other hand, if a not-null constraint exists, then verify that
8384 * it's compatible.
8385 */
8386 if (attTup->attnotnull)
8387 {
8390
8392 attnum);
8394 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8396
8398 if (!conForm->convalidated)
8399 ereport(ERROR,
8401 errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8402 NameStr(conForm->conname), RelationGetRelationName(rel)),
8403 errhint("You might need to validate it using %s.",
8404 "ALTER TABLE ... VALIDATE CONSTRAINT"));
8405 }
8406
8407 if (attTup->attidentity)
8408 ereport(ERROR,
8410 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8412
8413 if (attTup->atthasdef)
8414 ereport(ERROR,
8416 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8418
8419 attTup->attidentity = cdef->identity;
8420 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8421
8423 RelationGetRelid(rel),
8424 attTup->attnum);
8426 RelationGetRelid(rel), attnum);
8427 heap_freetuple(tuple);
8428
8430
8431 /*
8432 * Recurse to propagate the identity column to partitions. Identity is
8433 * not inherited in regular inheritance children.
8434 */
8435 if (recurse && ispartitioned)
8436 {
8437 List *children;
8438 ListCell *lc;
8439
8440 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8441
8442 foreach(lc, children)
8443 {
8445
8447 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8449 }
8450 }
8451
8452 return address;
8453}
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:8330

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

9699{
9700 bool check_rights;
9701 bool skip_build;
9702 bool quiet;
9703 ObjectAddress address;
9704
9706 Assert(!stmt->concurrent);
9707
9708 /* The IndexStmt has already been through transformIndexStmt */
9709 Assert(stmt->transformed);
9710
9711 /* suppress schema rights check when rebuilding existing index */
9713 /* skip index build if phase 3 will do it or we're reusing an old one */
9714 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9715 /* suppress notices when rebuilding existing index */
9716 quiet = is_rebuild;
9717
9718 address = DefineIndex(NULL,
9719 RelationGetRelid(rel),
9720 stmt,
9721 InvalidOid, /* no predefined OID */
9722 InvalidOid, /* no parent index */
9723 InvalidOid, /* no parent constraint */
9724 -1, /* total_parts unknown */
9725 true, /* is_alter_table */
9727 false, /* check_not_in_use - we did it already */
9728 skip_build,
9729 quiet);
9730
9731 /*
9732 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9733 * new index instead of building from scratch. Restore associated fields.
9734 * This may store InvalidSubTransactionId in both fields, in which case
9735 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9736 * this after the CCI that made catalog rows visible to any rebuild. The
9737 * DROP of the old edition of this index will have scheduled the storage
9738 * for deletion at commit, so cancel that pending deletion.
9739 */
9740 if (RelFileNumberIsValid(stmt->oldNumber))
9741 {
9742 Relation irel = index_open(address.objectId, NoLock);
9743
9744 irel->rd_createSubid = stmt->oldCreateSubid;
9745 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9747 index_close(irel, NoLock);
9748 }
9749
9750 return address;
9751}
void index_close(Relation relation, LOCKMODE lockmode)
Definition indexam.c:178
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition indexam.c:134
ObjectAddress DefineIndex(ParseState *pstate, Oid tableId, const 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:545
#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 9782 of file tablecmds.c.

9784{
9785 Oid index_oid = stmt->indexOid;
9786 Relation indexRel;
9787 char *indexName;
9788 IndexInfo *indexInfo;
9789 char *constraintName;
9790 char constraintType;
9791 ObjectAddress address;
9792 uint16 flags;
9793
9796 Assert(stmt->isconstraint);
9797
9798 /*
9799 * Doing this on partitioned tables is not a simple feature to implement,
9800 * so let's punt for now.
9801 */
9802 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9803 ereport(ERROR,
9805 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9806
9808
9810
9811 indexInfo = BuildIndexInfo(indexRel);
9812
9813 /* this should have been checked at parse time */
9814 if (!indexInfo->ii_Unique)
9815 elog(ERROR, "index \"%s\" is not unique", indexName);
9816
9817 /*
9818 * Determine name to assign to constraint. We require a constraint to
9819 * have the same name as the underlying index; therefore, use the index's
9820 * existing name as the default constraint name, and if the user
9821 * explicitly gives some other name for the constraint, rename the index
9822 * to match.
9823 */
9824 constraintName = stmt->idxname;
9825 if (constraintName == NULL)
9827 else if (strcmp(constraintName, indexName) != 0)
9828 {
9830 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9833 }
9834
9835 /* Extra checks needed if making primary key */
9836 if (stmt->primary)
9837 index_check_primary_key(rel, indexInfo, true, stmt);
9838
9839 /* Note we currently don't support EXCLUSION constraints here */
9840 if (stmt->primary)
9842 else
9844
9845 /* Create the catalog entries for the constraint */
9848 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9849 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9851
9852 address = index_constraint_create(rel,
9853 index_oid,
9854 InvalidOid,
9855 indexInfo,
9858 flags,
9860 false); /* is_internal */
9861
9862 index_close(indexRel, NoLock);
9863
9864 return address;
9865}
uint16_t uint16
Definition c.h:623
ObjectAddress index_constraint_create(Relation heapRelation, Oid indexRelationId, Oid parentConstraintId, const IndexInfo *indexInfo, const char *constraintName, char constraintType, uint16 constr_flags, bool allow_system_table_mods, bool is_internal)
Definition index.c:1903
IndexInfo * BuildIndexInfo(Relation index)
Definition index.c:2446
void index_check_primary_key(Relation heapRel, const IndexInfo *indexInfo, bool is_alter_table, const IndexStmt *stmt)
Definition index.c:203
#define INDEX_CONSTR_CREATE_UPDATE_INDEX
Definition index.h:101
#define INDEX_CONSTR_CREATE_REMOVE_OLD_DEPS
Definition index.h:102
#define INDEX_CONSTR_CREATE_DEFERRABLE
Definition index.h:99
#define INDEX_CONSTR_CREATE_MARK_AS_PRIMARY
Definition index.h:98
#define INDEX_CONSTR_CREATE_INIT_DEFERRED
Definition index.h:100
bool ii_Unique
Definition execnodes.h:214
void RenameRelationInternal(Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
Definition tablecmds.c:4327

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

17514{
17516 List *children;
17517 ObjectAddress address;
17518 const char *trigger_name;
17519
17520 /*
17521 * A self-exclusive lock is needed here. See the similar case in
17522 * MergeAttributes() for a full explanation.
17523 */
17525
17526 /*
17527 * Must be owner of both parent and child -- child was checked by
17528 * ATSimplePermissions call in ATPrepCmd
17529 */
17532
17533 /* Permanent rels cannot inherit from temporary ones */
17534 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17535 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17536 ereport(ERROR,
17538 errmsg("cannot inherit from temporary relation \"%s\"",
17540
17541 /* If parent rel is temp, it must belong to this session */
17543 ereport(ERROR,
17545 errmsg("cannot inherit from temporary relation of another session")));
17546
17547 /* Ditto for the child */
17549 ereport(ERROR,
17551 errmsg("cannot inherit to temporary relation of another session")));
17552
17553 /* Prevent partitioned tables from becoming inheritance parents */
17554 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17555 ereport(ERROR,
17557 errmsg("cannot inherit from partitioned table \"%s\"",
17558 parent->relname)));
17559
17560 /* Likewise for partitions */
17561 if (parent_rel->rd_rel->relispartition)
17562 ereport(ERROR,
17564 errmsg("cannot inherit from a partition")));
17565
17566 /*
17567 * Prevent circularity by seeing if proposed parent inherits from child.
17568 * (In particular, this disallows making a rel inherit from itself.)
17569 *
17570 * This is not completely bulletproof because of race conditions: in
17571 * multi-level inheritance trees, someone else could concurrently be
17572 * making another inheritance link that closes the loop but does not join
17573 * either of the rels we have locked. Preventing that seems to require
17574 * exclusive locks on the entire inheritance tree, which is a cure worse
17575 * than the disease. find_all_inheritors() will cope with circularity
17576 * anyway, so don't sweat it too much.
17577 *
17578 * We use weakest lock we can on child's children, namely AccessShareLock.
17579 */
17582
17584 ereport(ERROR,
17586 errmsg("circular inheritance not allowed"),
17587 errdetail("\"%s\" is already a child of \"%s\".",
17588 parent->relname,
17590
17591 /*
17592 * If child_rel has row-level triggers with transition tables, we
17593 * currently don't allow it to become an inheritance child. See also
17594 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17595 */
17597 if (trigger_name != NULL)
17598 ereport(ERROR,
17600 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17602 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17603
17604 /* OK to create inheritance */
17606
17609
17610 /* keep our lock on the parent relation until commit */
17612
17613 return address;
17614}
#define RELATION_IS_OTHER_TEMP(relation)
Definition rel.h:678
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:2279

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

18465{
18466 Oid relid = RelationGetRelid(rel);
18469 Oid typeid;
18472 SysScanDesc scan;
18475 type_attno;
18479 typeobj;
18481
18482 /* Validate the type. */
18483 typetuple = typenameType(NULL, ofTypename, NULL);
18486 typeid = typeform->oid;
18487
18488 /* Fail if the table has any inheritance parents. */
18490 ScanKeyInit(&key,
18493 ObjectIdGetDatum(relid));
18495 true, NULL, 1, &key);
18497 ereport(ERROR,
18499 errmsg("typed tables cannot inherit")));
18500 systable_endscan(scan);
18502
18503 /*
18504 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18505 * require that the order also match. However, attnotnull need not match.
18506 */
18509 table_attno = 1;
18510 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18511 {
18513 table_attr;
18514 const char *type_attname,
18516
18517 /* Get the next non-dropped type attribute. */
18519 if (type_attr->attisdropped)
18520 continue;
18521 type_attname = NameStr(type_attr->attname);
18522
18523 /* Get the next non-dropped table attribute. */
18524 do
18525 {
18526 if (table_attno > tableTupleDesc->natts)
18527 ereport(ERROR,
18529 errmsg("table is missing column \"%s\"",
18530 type_attname)));
18532 table_attno++;
18533 } while (table_attr->attisdropped);
18534 table_attname = NameStr(table_attr->attname);
18535
18536 /* Compare name. */
18538 ereport(ERROR,
18540 errmsg("table has column \"%s\" where type requires \"%s\"",
18542
18543 /* Compare type. */
18544 if (table_attr->atttypid != type_attr->atttypid ||
18545 table_attr->atttypmod != type_attr->atttypmod ||
18546 table_attr->attcollation != type_attr->attcollation)
18547 ereport(ERROR,
18549 errmsg("table \"%s\" has different type for column \"%s\"",
18551 }
18553
18554 /* Any remaining columns at the end of the table had better be dropped. */
18555 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18556 {
18558 table_attno - 1);
18559
18560 if (!table_attr->attisdropped)
18561 ereport(ERROR,
18563 errmsg("table has extra column \"%s\"",
18564 NameStr(table_attr->attname))));
18565 }
18566
18567 /* If the table was already typed, drop the existing dependency. */
18568 if (rel->rd_rel->reloftype)
18569 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18571
18572 /* Record a dependency on the new type. */
18573 tableobj.classId = RelationRelationId;
18574 tableobj.objectId = relid;
18575 tableobj.objectSubId = 0;
18576 typeobj.classId = TypeRelationId;
18577 typeobj.objectId = typeid;
18578 typeobj.objectSubId = 0;
18580
18581 /* Update pg_class.reloftype */
18585 elog(ERROR, "cache lookup failed for relation %u", relid);
18586 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18588
18590
18593
18595
18596 return typeobj;
18597}
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:7233
#define ReleaseTupleDesc(tupdesc)
Definition tupdesc.h:240
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition typcache.c:1940

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

9763{
9764 ObjectAddress address;
9765
9767
9768 /* The CreateStatsStmt has already been through transformStatsStmt */
9769 Assert(stmt->transformed);
9770
9771 address = CreateStatistics(stmt, !is_rebuild);
9772
9773 return address;
9774}
ObjectAddress CreateStatistics(CreateStatsStmt *stmt, bool check_rights)
Definition statscmds.c:64

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

Referenced by ATExecCmd().

◆ ATExecAlterCheckConstrEnforceability()

static bool ATExecAlterCheckConstrEnforceability ( List **  wqueue,
ATAlterConstraint cmdcon,
Relation  conrel,
HeapTuple  contuple,
bool  recurse,
bool  recursing,
LOCKMODE  lockmode 
)
static

Definition at line 12647 of file tablecmds.c.

12650{
12652 Relation rel;
12653 bool changed = false;
12654 List *children = NIL;
12655
12656 /* Since this function recurses, it could be driven to stack overflow */
12658
12659 Assert(cmdcon->alterEnforceability);
12660
12662
12663 Assert(currcon->contype == CONSTRAINT_CHECK);
12664
12665 /*
12666 * Parent relation already locked by caller, children will be locked by
12667 * find_all_inheritors. So NoLock is fine here.
12668 */
12669 rel = table_open(currcon->conrelid, NoLock);
12670
12671 if (currcon->conenforced != cmdcon->is_enforced)
12672 {
12674 changed = true;
12675 }
12676
12677 /*
12678 * Note that we must recurse even when trying to change a check constraint
12679 * to not enforced if it is already not enforced, in case descendant
12680 * constraints might be enforced and need to be changed to not enforced.
12681 * Conversely, we should do nothing if a constraint is being set to
12682 * enforced and is already enforced, as descendant constraints cannot be
12683 * different in that case.
12684 */
12685 if (!cmdcon->is_enforced || changed)
12686 {
12687 /*
12688 * If we're recursing, the parent has already done this, so skip it.
12689 * Also, if the constraint is a NO INHERIT constraint, we shouldn't
12690 * try to look for it in the children.
12691 */
12692 if (!recursing && !currcon->connoinherit)
12693 children = find_all_inheritors(RelationGetRelid(rel),
12694 lockmode, NULL);
12695
12696 foreach_oid(childoid, children)
12697 {
12698 if (childoid == RelationGetRelid(rel))
12699 continue;
12700
12701 /*
12702 * If we are told not to recurse, there had better not be any
12703 * child tables, because we can't change constraint enforceability
12704 * on the parent unless we have changed enforceability for all
12705 * child.
12706 */
12707 if (!recurse)
12708 ereport(ERROR,
12710 errmsg("constraint must be altered on child tables too"),
12711 errhint("Do not specify the ONLY keyword."));
12712
12714 childoid, false, true,
12715 lockmode);
12716 }
12717 }
12718
12719 /*
12720 * Tell Phase 3 to check that the constraint is satisfied by existing
12721 * rows. We only need do this when altering the constraint from NOT
12722 * ENFORCED to ENFORCED.
12723 */
12724 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12725 !currcon->conenforced &&
12726 cmdcon->is_enforced)
12727 {
12728 AlteredTableInfo *tab;
12730 Datum val;
12731 char *conbin;
12732
12734 newcon->name = pstrdup(NameStr(currcon->conname));
12735 newcon->contype = CONSTR_CHECK;
12736
12741
12742 /* Find or create work queue entry for this table */
12743 tab = ATGetQueueEntry(wqueue, rel);
12744 tab->constraints = lappend(tab->constraints, newcon);
12745 }
12746
12747 table_close(rel, NoLock);
12748
12749 return changed;
12750}
#define TextDatumGetCString(d)
Definition builtins.h:99
long val
Definition informix.c:689
void * stringToNode(const char *str)
Definition read.c:90
Node * expand_generated_columns_in_expr(Node *node, Relation rel, int rt_index)
Datum SysCacheGetAttrNotNull(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber)
Definition syscache.c:626
static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)
static void AlterCheckConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Oid conrelid, bool recurse, bool recursing, LOCKMODE lockmode)

References AlterCheckConstrEnforceabilityRecurse(), AlterConstrUpdateConstraintEntry(), Assert, ATGetQueueEntry(), check_stack_depth(), CONSTR_CHECK, AlteredTableInfo::constraints, ereport, errcode(), errhint(), errmsg, ERROR, expand_generated_columns_in_expr(), fb(), find_all_inheritors(), foreach_oid, Form_pg_constraint, GETSTRUCT(), lappend(), NameStr, NIL, NoLock, palloc0_object, pstrdup(), RelationData::rd_rel, RelationGetRelid, stringToNode(), SysCacheGetAttrNotNull(), table_close(), table_open(), TextDatumGetCString, and val.

Referenced by AlterCheckConstrEnforceabilityRecurse(), and ATExecAlterConstraintInternal().

◆ ATExecAlterColumnGenericOptions()

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

Definition at line 16211 of file tablecmds.c.

16215{
16218 ForeignServer *server;
16220 HeapTuple tuple;
16221 HeapTuple newtuple;
16222 bool isnull;
16226 Datum datum;
16230 ObjectAddress address;
16231
16232 if (options == NIL)
16233 return InvalidObjectAddress;
16234
16235 /* First, determine FDW validator associated to the foreign table. */
16238 if (!HeapTupleIsValid(tuple))
16239 ereport(ERROR,
16241 errmsg("foreign table \"%s\" does not exist",
16244 server = GetForeignServer(fttableform->ftserver);
16245 fdw = GetForeignDataWrapper(server->fdwid);
16246
16248 ReleaseSysCache(tuple);
16249
16252 if (!HeapTupleIsValid(tuple))
16253 ereport(ERROR,
16255 errmsg("column \"%s\" of relation \"%s\" does not exist",
16257
16258 /* Prevent them from altering a system attribute */
16260 attnum = atttableform->attnum;
16261 if (attnum <= 0)
16262 ereport(ERROR,
16264 errmsg("cannot alter system column \"%s\"", colName)));
16265
16266
16267 /* Initialize buffers for new tuple values */
16268 memset(repl_val, 0, sizeof(repl_val));
16269 memset(repl_null, false, sizeof(repl_null));
16270 memset(repl_repl, false, sizeof(repl_repl));
16271
16272 /* Extract the current options */
16273 datum = SysCacheGetAttr(ATTNAME,
16274 tuple,
16276 &isnull);
16277 if (isnull)
16278 datum = PointerGetDatum(NULL);
16279
16280 /* Transform the options */
16282 datum,
16283 options,
16284 fdw->fdwvalidator);
16285
16286 if (DatumGetPointer(datum) != NULL)
16288 else
16290
16292
16293 /* Everything looks good - update the tuple */
16294
16295 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16297
16298 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16299
16301 RelationGetRelid(rel),
16302 atttableform->attnum);
16304 RelationGetRelid(rel), attnum);
16305
16306 ReleaseSysCache(tuple);
16307
16309
16310 heap_freetuple(newtuple);
16311
16312 return address;
16313}
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition foreign.c:39
ForeignServer * GetForeignServer(Oid serverid)
Definition foreign.c:114
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:1118
END_CATALOG_STRUCT typedef FormData_pg_foreign_table * Form_pg_foreign_table
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
#define PointerGetDatum(X)
Definition postgres.h:354
Oid rd_id
Definition rel.h:113
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition syscache.c:476
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:596

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

14984{
14985 char *colName = cmd->name;
14986 ColumnDef *def = (ColumnDef *) cmd->def;
14987 TypeName *typeName = def->typeName;
14990 attOldTup;
14994 Oid targettype;
14995 int32 targettypmod;
15000 ScanKeyData key[3];
15001 SysScanDesc scan;
15003 ObjectAddress address;
15004
15005 /*
15006 * Clear all the missing values if we're rewriting the table, since this
15007 * renders them pointless.
15008 */
15009 if (tab->rewrite)
15010 {
15012
15016 /* make sure we don't conflict with later attribute modifications */
15018 }
15019
15021
15022 /* Look up the target column */
15024 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
15025 ereport(ERROR,
15027 errmsg("column \"%s\" of relation \"%s\" does not exist",
15030 attnum = attTup->attnum;
15032
15033 /* Check for multiple ALTER TYPE on same column --- can't cope */
15034 if (attTup->atttypid != attOldTup->atttypid ||
15035 attTup->atttypmod != attOldTup->atttypmod)
15036 ereport(ERROR,
15038 errmsg("cannot alter type of column \"%s\" twice",
15039 colName)));
15040
15041 /* Look up the target type (should not fail, since prep found it) */
15042 typeTuple = typenameType(NULL, typeName, &targettypmod);
15044 targettype = tform->oid;
15045 /* And the collation */
15046 targetcollid = GetColumnDefCollation(NULL, def, targettype);
15047
15048 /*
15049 * If there is a default expression for the column, get it and ensure we
15050 * can coerce it to the new datatype. (We must do this before changing
15051 * the column type, because build_column_default itself will try to
15052 * coerce, and will not issue the error message we want if it fails.)
15053 *
15054 * We remove any implicit coercion steps at the top level of the old
15055 * default expression; this has been agreed to satisfy the principle of
15056 * least surprise. (The conversion to the new column type should act like
15057 * it started from what the user sees as the stored expression, and the
15058 * implicit coercions aren't going to be shown.)
15059 */
15060 if (attTup->atthasdef)
15061 {
15065 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
15067 targettype, targettypmod,
15070 -1);
15071 if (defaultexpr == NULL)
15072 {
15073 if (attTup->attgenerated)
15074 ereport(ERROR,
15076 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
15077 colName, format_type_be(targettype))));
15078 else
15079 ereport(ERROR,
15081 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
15082 colName, format_type_be(targettype))));
15083 }
15084 }
15085 else
15086 defaultexpr = NULL;
15087
15088 /*
15089 * Find everything that depends on the column (constraints, indexes, etc),
15090 * and record enough information to let us recreate the objects.
15091 *
15092 * The actual recreation does not happen here, but only after we have
15093 * performed all the individual ALTER TYPE operations. We have to save
15094 * the info before executing ALTER TYPE, though, else the deparser will
15095 * get confused.
15096 */
15098
15099 /*
15100 * Now scan for dependencies of this column on other things. The only
15101 * things we should find are the dependency on the column datatype and
15102 * possibly a collation dependency. Those can be removed.
15103 */
15105
15106 ScanKeyInit(&key[0],
15110 ScanKeyInit(&key[1],
15114 ScanKeyInit(&key[2],
15118
15120 NULL, 3, key);
15121
15123 {
15126
15127 foundObject.classId = foundDep->refclassid;
15128 foundObject.objectId = foundDep->refobjid;
15129 foundObject.objectSubId = foundDep->refobjsubid;
15130
15131 if (foundDep->deptype != DEPENDENCY_NORMAL)
15132 elog(ERROR, "found unexpected dependency type '%c'",
15133 foundDep->deptype);
15134 if (!(foundDep->refclassid == TypeRelationId &&
15135 foundDep->refobjid == attTup->atttypid) &&
15136 !(foundDep->refclassid == CollationRelationId &&
15137 foundDep->refobjid == attTup->attcollation))
15138 elog(ERROR, "found unexpected dependency for column: %s",
15140
15141 CatalogTupleDelete(depRel, &depTup->t_self);
15142 }
15143
15144 systable_endscan(scan);
15145
15147
15148 /*
15149 * Here we go --- change the recorded column type and collation. (Note
15150 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
15151 * fix up the missing value if any.
15152 */
15153 if (attTup->atthasmissing)
15154 {
15156 bool missingNull;
15157
15158 /* if rewrite is true the missing value should already be cleared */
15159 Assert(tab->rewrite == 0);
15160
15161 /* Get the missing value datum */
15164 attrelation->rd_att,
15165 &missingNull);
15166
15167 /* if it's a null array there is nothing to do */
15168
15169 if (!missingNull)
15170 {
15171 /*
15172 * Get the datum out of the array and repack it in a new array
15173 * built with the new type data. We assume that since the table
15174 * doesn't need rewriting, the actual Datum doesn't need to be
15175 * changed, only the array metadata.
15176 */
15177
15178 int one = 1;
15179 bool isNull;
15181 bool nullsAtt[Natts_pg_attribute] = {0};
15182 bool replacesAtt[Natts_pg_attribute] = {0};
15184
15186 1,
15187 &one,
15188 0,
15189 attTup->attlen,
15190 attTup->attbyval,
15191 attTup->attalign,
15192 &isNull);
15194 1,
15195 targettype,
15196 tform->typlen,
15197 tform->typbyval,
15198 tform->typalign));
15199
15203
15207 heapTup = newTup;
15209 }
15210 }
15211
15212 attTup->atttypid = targettype;
15213 attTup->atttypmod = targettypmod;
15214 attTup->attcollation = targetcollid;
15215 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
15216 ereport(ERROR,
15218 errmsg("too many array dimensions"));
15219 attTup->attndims = list_length(typeName->arrayBounds);
15220 attTup->attlen = tform->typlen;
15221 attTup->attbyval = tform->typbyval;
15222 attTup->attalign = tform->typalign;
15223 attTup->attstorage = tform->typstorage;
15224 attTup->attcompression = InvalidCompressionMethod;
15225
15227
15229
15231
15232 /* Install dependencies on new datatype and collation */
15235
15236 /*
15237 * Drop any pg_statistic entry for the column, since it's now wrong type
15238 */
15240
15242 RelationGetRelid(rel), attnum);
15243
15244 /*
15245 * Update the default, if present, by brute force --- remove and re-add
15246 * the default. Probably unsafe to take shortcuts, since the new version
15247 * may well have additional dependencies. (It's okay to do this now,
15248 * rather than after other ALTER TYPE commands, since the default won't
15249 * depend on other column types.)
15250 */
15251 if (defaultexpr)
15252 {
15253 /*
15254 * If it's a GENERATED default, drop its dependency records, in
15255 * particular its INTERNAL dependency on the column, which would
15256 * otherwise cause dependency.c to refuse to perform the deletion.
15257 */
15258 if (attTup->attgenerated)
15259 {
15261
15262 if (!OidIsValid(attrdefoid))
15263 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15264 RelationGetRelid(rel), attnum);
15266 }
15267
15268 /*
15269 * Make updates-so-far visible, particularly the new pg_attribute row
15270 * which will be updated again.
15271 */
15273
15274 /*
15275 * We use RESTRICT here for safety, but at present we do not expect
15276 * anything to depend on the default.
15277 */
15279 true);
15280
15281 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15282 }
15283
15285 RelationGetRelid(rel), attnum);
15286
15287 /* Cleanup */
15289
15290 return address;
15291}
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:670
void RelationClearMissing(Relation rel)
Definition heap.c:1984
void RemoveStatistics(Oid relid, AttrNumber attnum)
Definition heap.c:3515
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:710
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:314
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
List * arrayBounds
Definition parsenodes.h:294
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 12276 of file tablecmds.c.

12278{
12281 SysScanDesc scan;
12282 ScanKeyData skey[3];
12285 ObjectAddress address;
12286
12287 /*
12288 * Disallow altering ONLY a partitioned table, as it would make no sense.
12289 * This is okay for legacy inheritance.
12290 */
12291 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12292 ereport(ERROR,
12294 errmsg("constraint must be altered in child tables too"),
12295 errhint("Do not specify the ONLY keyword."));
12296
12297
12300
12301 /*
12302 * Find and check the target constraint
12303 */
12304 ScanKeyInit(&skey[0],
12308 ScanKeyInit(&skey[1],
12312 ScanKeyInit(&skey[2],
12315 CStringGetDatum(cmdcon->conname));
12317 true, NULL, 3, skey);
12318
12319 /* There can be at most one matching row */
12321 ereport(ERROR,
12323 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12324 cmdcon->conname, RelationGetRelationName(rel))));
12325
12327 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12328 ereport(ERROR,
12330 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12331 cmdcon->conname, RelationGetRelationName(rel))));
12332 if (cmdcon->alterEnforceability &&
12333 (currcon->contype != CONSTRAINT_FOREIGN && currcon->contype != CONSTRAINT_CHECK))
12334 ereport(ERROR,
12336 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12337 cmdcon->conname, RelationGetRelationName(rel)),
12338 errhint("Only foreign key and check constraints can change enforceability.")));
12339 if (cmdcon->alterInheritability &&
12340 currcon->contype != CONSTRAINT_NOTNULL)
12341 ereport(ERROR,
12343 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12344 cmdcon->conname, RelationGetRelationName(rel)));
12345 if (cmdcon->alterInheritability &&
12346 cmdcon->noinherit && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12347 ereport(ERROR,
12349 errmsg("not-null constraint \"%s\" on partitioned table \"%s\" cannot be NO INHERIT",
12350 cmdcon->conname, RelationGetRelationName(rel)));
12351
12352 /* Refuse to modify inheritability of inherited constraints */
12353 if (cmdcon->alterInheritability &&
12354 cmdcon->noinherit && currcon->coninhcount > 0)
12355 ereport(ERROR,
12357 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12358 NameStr(currcon->conname),
12360
12361 /*
12362 * If it's not the topmost constraint, raise an error.
12363 *
12364 * Altering a non-topmost constraint leaves some triggers untouched, since
12365 * they are not directly connected to this constraint; also, pg_dump would
12366 * ignore the deferrability status of the individual constraint, since it
12367 * only dumps topmost constraints. Avoid these problems by refusing this
12368 * operation and telling the user to alter the parent constraint instead.
12369 */
12370 if (OidIsValid(currcon->conparentid))
12371 {
12372 HeapTuple tp;
12373 Oid parent = currcon->conparentid;
12374 char *ancestorname = NULL;
12375 char *ancestortable = NULL;
12376
12377 /* Loop to find the topmost constraint */
12379 {
12381
12382 /* If no parent, this is the constraint we want */
12383 if (!OidIsValid(contup->conparentid))
12384 {
12385 ancestorname = pstrdup(NameStr(contup->conname));
12386 ancestortable = get_rel_name(contup->conrelid);
12387 ReleaseSysCache(tp);
12388 break;
12389 }
12390
12391 parent = contup->conparentid;
12392 ReleaseSysCache(tp);
12393 }
12394
12395 ereport(ERROR,
12397 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12398 cmdcon->conname, RelationGetRelationName(rel)),
12400 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12401 cmdcon->conname, ancestorname, ancestortable) : 0,
12402 errhint("You may alter the constraint it derives from instead.")));
12403 }
12404
12405 address = InvalidObjectAddress;
12406
12407 /*
12408 * Do the actual catalog work, and recurse if necessary.
12409 */
12411 contuple, recurse, lockmode))
12413
12414 systable_endscan(scan);
12415
12418
12419 return address;
12420}
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 12427 of file tablecmds.c.

12431{
12433 bool changed = false;
12434 List *otherrelids = NIL;
12435
12437
12438 /*
12439 * Do the catalog work for the enforceability or deferrability change,
12440 * recurse if necessary.
12441 *
12442 * Note that even if deferrability is requested to be altered along with
12443 * enforceability, we don't need to explicitly update multiple entries in
12444 * pg_trigger related to deferrability.
12445 *
12446 * Modifying foreign key enforceability involves either creating or
12447 * dropping the trigger, during which the deferrability setting will be
12448 * adjusted automatically.
12449 */
12450 if (cmdcon->alterEnforceability)
12451 {
12452 if (currcon->contype == CONSTRAINT_FOREIGN)
12454 currcon->conrelid,
12455 currcon->confrelid,
12456 contuple, lockmode,
12459 else if (currcon->contype == CONSTRAINT_CHECK)
12461 contuple, recurse, false,
12462 lockmode);
12463 }
12464 else if (cmdcon->alterDeferrability &&
12466 contuple, recurse, &otherrelids,
12467 lockmode))
12468 {
12469 /*
12470 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12471 * the relations having the constraint itself; here we also invalidate
12472 * for relations that have any triggers that are part of the
12473 * constraint.
12474 */
12475 foreach_oid(relid, otherrelids)
12477
12478 changed = true;
12479 }
12480
12481 /*
12482 * Do the catalog work for the inheritability change.
12483 */
12484 if (cmdcon->alterInheritability &&
12486 lockmode))
12487 changed = true;
12488
12489 return changed;
12490}
static bool ATExecAlterConstrInheritability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation rel, HeapTuple contuple, LOCKMODE lockmode)

References ATExecAlterCheckConstrEnforceability(), ATExecAlterConstrDeferrability(), ATExecAlterConstrInheritability(), ATExecAlterFKConstrEnforceability(), 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 12811 of file tablecmds.c.

12815{
12817 Oid refrelid;
12818 bool changed = false;
12819
12820 /* since this function recurses, it could be driven to stack overflow */
12822
12823 Assert(cmdcon->alterDeferrability);
12824
12826 refrelid = currcon->confrelid;
12827
12828 /* Should be foreign key constraint */
12829 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12830
12831 /*
12832 * If called to modify a constraint that's already in the desired state,
12833 * silently do nothing.
12834 */
12835 if (currcon->condeferrable != cmdcon->deferrable ||
12836 currcon->condeferred != cmdcon->initdeferred)
12837 {
12839 changed = true;
12840
12841 /*
12842 * Now we need to update the multiple entries in pg_trigger that
12843 * implement the constraint.
12844 */
12846 cmdcon->deferrable,
12847 cmdcon->initdeferred, otherrelids);
12848 }
12849
12850 /*
12851 * If the table at either end of the constraint is partitioned, we need to
12852 * handle every constraint that is a child of this one.
12853 */
12854 if (recurse && changed &&
12855 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12858 contuple, recurse, otherrelids,
12859 lockmode);
12860
12861 return changed;
12862}
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().

◆ ATExecAlterConstrInheritability()

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

Definition at line 12868 of file tablecmds.c.

12871{
12874 char *colName;
12875 List *children;
12876
12877 Assert(cmdcon->alterInheritability);
12878
12880
12881 /* The current implementation only works for NOT NULL constraints */
12882 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12883
12884 /*
12885 * If called to modify a constraint that's already in the desired state,
12886 * silently do nothing.
12887 */
12888 if (cmdcon->noinherit == currcon->connoinherit)
12889 return false;
12890
12893
12894 /* Fetch the column number and name */
12896 colName = get_attname(currcon->conrelid, colNum, false);
12897
12898 /*
12899 * Propagate the change to children. For this subcommand type we don't
12900 * recursively affect children, just the immediate level.
12901 */
12903 lockmode);
12904 foreach_oid(childoid, children)
12905 {
12906 ObjectAddress addr;
12907
12908 if (cmdcon->noinherit)
12909 {
12912
12914 if (!childtup)
12915 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12916 colName, childoid);
12918 Assert(childcon->coninhcount > 0);
12919 childcon->coninhcount--;
12920 childcon->conislocal = true;
12923 }
12924 else
12925 {
12927
12928 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12929 colName, true, true, lockmode);
12930 if (OidIsValid(addr.objectId))
12933 }
12934 }
12935
12936 return true;
12937}
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition lsyscache.c:1045
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:8003

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

◆ ATExecAlterFKConstrEnforceability()

static bool ATExecAlterFKConstrEnforceability ( 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 12505 of file tablecmds.c.

12513{
12515 Oid conoid;
12516 Relation rel;
12517 bool changed = false;
12518
12519 /* Since this function recurses, it could be driven to stack overflow */
12521
12522 Assert(cmdcon->alterEnforceability);
12523
12525 conoid = currcon->oid;
12526
12527 /* Should be foreign key constraint */
12528 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12529
12530 rel = table_open(currcon->conrelid, lockmode);
12531
12532 if (currcon->conenforced != cmdcon->is_enforced)
12533 {
12535 changed = true;
12536 }
12537
12538 /* Drop triggers */
12539 if (!cmdcon->is_enforced)
12540 {
12541 /*
12542 * When setting a constraint to NOT ENFORCED, the constraint triggers
12543 * need to be dropped. Therefore, we must process the child relations
12544 * first, followed by the parent, to account for dependencies.
12545 */
12546 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12550 lockmode, InvalidOid, InvalidOid,
12552
12553 /* Drop all the triggers */
12555 }
12556 else if (changed) /* Create triggers */
12557 {
12562
12563 /* Prepare the minimal information required for trigger creation. */
12565
12566 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12567 fkconstraint->fk_matchtype = currcon->confmatchtype;
12568 fkconstraint->fk_upd_action = currcon->confupdtype;
12569 fkconstraint->fk_del_action = currcon->confdeltype;
12570 fkconstraint->deferrable = currcon->condeferrable;
12571 fkconstraint->initdeferred = currcon->condeferred;
12572
12573 /* Create referenced triggers */
12574 if (currcon->conrelid == fkrelid)
12576 currcon->confrelid,
12578 conoid,
12579 currcon->conindid,
12584
12585 /* Create referencing triggers */
12586 if (currcon->confrelid == pkrelid)
12588 pkrelid,
12590 conoid,
12591 currcon->conindid,
12596
12597 /*
12598 * Tell Phase 3 to check that the constraint is satisfied by existing
12599 * rows. Only applies to leaf partitions, and (for constraints that
12600 * reference a partitioned table) only if this is not one of the
12601 * pg_constraint rows that exist solely to support action triggers.
12602 */
12603 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12604 currcon->confrelid == pkrelid)
12605 {
12606 AlteredTableInfo *tab;
12608
12610 newcon->name = fkconstraint->conname;
12611 newcon->contype = CONSTR_FOREIGN;
12612 newcon->refrelid = currcon->confrelid;
12613 newcon->refindid = currcon->conindid;
12614 newcon->conid = currcon->oid;
12615 newcon->qual = (Node *) fkconstraint;
12616
12617 /* Find or create work queue entry for this table */
12618 tab = ATGetQueueEntry(wqueue, rel);
12619 tab->constraints = lappend(tab->constraints, newcon);
12620 }
12621
12622 /*
12623 * If the table at either end of the constraint is partitioned, we
12624 * need to recurse and create triggers for each constraint that is a
12625 * child of this one.
12626 */
12627 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12631 lockmode,
12636 }
12637
12638 table_close(rel, NoLock);
12639
12640 return changed;
12641}
static void DropForeignKeyConstraintTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid)
static void AlterFKConstrEnforceabilityRecurse(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 AlterConstrUpdateConstraintEntry(), AlterFKConstrEnforceabilityRecurse(), 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 AlterFKConstrEnforceabilityRecurse(), and ATExecAlterConstraintInternal().

◆ ATExecAttachPartition()

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

Definition at line 20537 of file tablecmds.c.

20539{
20541 catalog;
20544 SysScanDesc scan;
20546 AttrNumber attno;
20547 int natts;
20549 ObjectAddress address;
20550 const char *trigger_name;
20554 ParseState *pstate = make_parsestate(NULL);
20555
20556 pstate->p_sourcetext = context->queryString;
20557
20558 /*
20559 * We must lock the default partition if one exists, because attaching a
20560 * new partition will change its partition constraint.
20561 */
20566
20568
20569 /*
20570 * XXX I think it'd be a good idea to grab locks on all tables referenced
20571 * by FKs at this point also.
20572 */
20573
20574 /*
20575 * Must be owner of both parent and source table -- parent was checked by
20576 * ATSimplePermissions call in ATPrepCmd
20577 */
20580
20581 /* A partition can only have one parent */
20582 if (attachrel->rd_rel->relispartition)
20583 ereport(ERROR,
20585 errmsg("\"%s\" is already a partition",
20587
20588 if (OidIsValid(attachrel->rd_rel->reloftype))
20589 ereport(ERROR,
20591 errmsg("cannot attach a typed table as partition")));
20592
20593 /*
20594 * Disallow attaching a partition if the table is referenced in a
20595 * publication EXCEPT clause. Changing the partition hierarchy could alter
20596 * the effective publication membership.
20597 */
20599 if (exceptpuboids != NIL)
20600 {
20601 bool first = true;
20603
20605
20607 {
20608 char *pubname = get_publication_name(pubid, false);
20609
20610 if (!first)
20611 {
20612 /*
20613 * translator: This is a separator in a list of publication
20614 * names.
20615 */
20617 }
20618
20619 first = false;
20620
20621 appendStringInfo(&pubnames, _("\"%s\""), pubname);
20622 }
20623
20624 ereport(ERROR,
20626 errmsg_plural("cannot attach table \"%s\" as partition because it is referenced in publication %s EXCEPT clause",
20627 "cannot attach table \"%s\" as partition because it is referenced in publications %s EXCEPT clause",
20630 pubnames.data),
20631 errdetail("The publication EXCEPT clause cannot contain tables that are partitions."),
20632 errhint("Change the publication's EXCEPT clause using ALTER PUBLICATION ... SET ALL TABLES."));
20633 }
20634
20636
20637 /*
20638 * Table being attached should not already be part of inheritance; either
20639 * as a child table...
20640 */
20647 NULL, 1, &skey);
20649 ereport(ERROR,
20651 errmsg("cannot attach inheritance child as partition")));
20652 systable_endscan(scan);
20653
20654 /* ...or as a parent table (except the case when it is partitioned) */
20660 1, &skey);
20662 attachrel->rd_rel->relkind == RELKIND_RELATION)
20663 ereport(ERROR,
20665 errmsg("cannot attach inheritance parent as partition")));
20666 systable_endscan(scan);
20668
20669 /*
20670 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20671 * particular, this disallows making a rel a partition of itself.)
20672 *
20673 * We do that by checking if rel is a member of the list of attachrel's
20674 * partitions provided the latter is partitioned at all. We want to avoid
20675 * having to construct this list again, so we request the strongest lock
20676 * on all partitions. We need the strongest lock, because we may decide
20677 * to scan them if we find out that the table being attached (or its leaf
20678 * partitions) may contain rows that violate the partition constraint. If
20679 * the table has a constraint that would prevent such rows, which by
20680 * definition is present in all the partitions, we need not scan the
20681 * table, nor its partitions. But we cannot risk a deadlock by taking a
20682 * weaker lock now and the stronger one only when needed.
20683 */
20687 ereport(ERROR,
20689 errmsg("circular inheritance not allowed"),
20690 errdetail("\"%s\" is already a child of \"%s\".",
20693
20694 /* If the parent is permanent, so must be all of its partitions. */
20695 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20696 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20697 ereport(ERROR,
20699 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20701
20702 /* Temp parent cannot have a partition that is itself not a temp */
20703 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20704 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20705 ereport(ERROR,
20707 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20709
20710 /* If the parent is temp, it must belong to this session */
20711 if (RELATION_IS_OTHER_TEMP(rel))
20712 ereport(ERROR,
20714 errmsg("cannot attach as partition of temporary relation of another session")));
20715
20716 /* Ditto for the partition */
20718 ereport(ERROR,
20720 errmsg("cannot attach temporary relation of another session as partition")));
20721
20722 /*
20723 * Check if attachrel has any identity columns or any columns that aren't
20724 * in the parent.
20725 */
20727 natts = tupleDesc->natts;
20728 for (attno = 1; attno <= natts; attno++)
20729 {
20731 char *attributeName = NameStr(attribute->attname);
20732
20733 /* Ignore dropped */
20734 if (attribute->attisdropped)
20735 continue;
20736
20737 if (attribute->attidentity)
20738 ereport(ERROR,
20740 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20742 errdetail("The new partition may not contain an identity column."));
20743
20744 /* Try to find the column in parent (matching on column name) */
20748 ereport(ERROR,
20750 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20753 errdetail("The new partition may contain only the columns present in parent.")));
20754 }
20755
20756 /*
20757 * If child_rel has row-level triggers with transition tables, we
20758 * currently don't allow it to become a partition. See also prohibitions
20759 * in ATExecAddInherit() and CreateTrigger().
20760 */
20762 if (trigger_name != NULL)
20763 ereport(ERROR,
20765 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20767 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20768
20769 /*
20770 * Check that the new partition's bound is valid and does not overlap any
20771 * of existing partitions of the parent - note that it does not return on
20772 * error.
20773 */
20775 cmd->bound, pstate);
20776
20778
20779 /*
20780 * Generate a partition constraint from the partition bound specification.
20781 * If the parent itself is a partition, make sure to include its
20782 * constraint as well.
20783 */
20785
20786 /*
20787 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20788 * since it's needed later to construct the constraint expression for
20789 * validating against the default partition, if any.
20790 */
20793
20794 /* Skip validation if there are no constraints to validate. */
20795 if (partConstraint)
20796 {
20797 /*
20798 * Run the partition quals through const-simplification similar to
20799 * check constraints. We skip canonicalize_qual, though, because
20800 * partition quals should be in canonical form already.
20801 */
20804 (Node *) partConstraint);
20805
20806 /* XXX this sure looks wrong */
20808
20809 /*
20810 * Adjust the generated constraint to match this partition's attribute
20811 * numbers.
20812 */
20814 rel);
20815
20816 /* Validate partition constraints against the table being attached. */
20818 false);
20819 }
20820
20821 /*
20822 * If we're attaching a partition other than the default partition and a
20823 * default one exists, then that partition's partition constraint changes,
20824 * so add an entry to the work queue to validate it, too. (We must not do
20825 * this when the partition being attached is the default one; we already
20826 * did it above!)
20827 */
20829 {
20832
20833 Assert(!cmd->bound->is_default);
20834
20835 /* we already hold a lock on the default partition */
20839
20840 /*
20841 * Map the Vars in the constraint expression from rel's attnos to
20842 * defaultrel's.
20843 */
20846 1, defaultrel, rel);
20848 defPartConstraint, true);
20849
20850 /* keep our lock until commit. */
20852 }
20853
20855
20856 /*
20857 * If the partition we just attached is partitioned itself, invalidate
20858 * relcache for all descendent partitions too to ensure that their
20859 * rd_partcheck expression trees are rebuilt; partitions already locked at
20860 * the beginning of this function.
20861 */
20862 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20863 {
20864 ListCell *l;
20865
20866 foreach(l, attachrel_children)
20867 {
20869 }
20870 }
20871
20872 /* keep our lock until commit */
20874
20875 return address;
20876}
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition clauses.c:2500
#define _(x)
Definition elog.c:96
int int int errmsg_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...) pg_attribute_printf(1
List * list_concat_copy(const List *list1, const List *list2)
Definition list.c:598
char * get_publication_name(Oid pubid, bool missing_ok)
Definition lsyscache.c:3998
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
List * GetRelationExcludedPublications(Oid relid)
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
const char * queryString
Definition utility.h:33
const char * p_sourcetext
Definition parse_node.h:214
PartitionBoundSpec * bound
RangeVar * name
#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, appendStringInfo(), appendStringInfoString(), 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(), errhint(), errmsg, errmsg_plural(), ERROR, eval_const_expressions(), fb(), find_all_inheritors(), FindTriggerIncompatibleWithInheritance(), foreach_oid, get_default_oid_from_partdesc(), get_proposed_default_constraint(), get_publication_name(), get_qual_from_partbound(), GetRelationExcludedPublications(), HeapTupleIsValid, initStringInfo(), PartitionBoundSpec::is_default, lfirst_oid, list_concat_copy(), list_free(), list_length(), list_make1, list_member_oid(), LockRelationOid(), make_ands_explicit(), make_parsestate(), map_partition_varattnos(), PartitionCmd::name, NameStr, NIL, 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 21895 of file tablecmds.c.

21896{
21899 Relation parentTbl;
21900 ObjectAddress address;
21901 Oid partIdxId;
21904
21905 /*
21906 * We need to obtain lock on the index 'name' to modify it, but we also
21907 * need to read its owning table's tuple descriptor -- so we need to lock
21908 * both. To avoid deadlocks, obtain lock on the table before doing so on
21909 * the index. Furthermore, we need to examine the parent table of the
21910 * partition, so lock that one too.
21911 */
21912 state.partitionOid = InvalidOid;
21913 state.parentTblOid = parentIdx->rd_index->indrelid;
21914 state.lockedParentTbl = false;
21915 partIdxId =
21918 &state);
21919 /* Not there? */
21920 if (!OidIsValid(partIdxId))
21921 ereport(ERROR,
21923 errmsg("index \"%s\" does not exist", name->relname)));
21924
21925 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21927
21928 /* we already hold locks on both tables, so this is safe: */
21929 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21930 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21931
21933
21934 /*
21935 * Check if the index is already attached to the correct parent,
21936 * ultimately attempting one round of validation if already the case.
21937 */
21938 currParent = partIdx->rd_rel->relispartition ?
21940 if (currParent != RelationGetRelid(parentIdx))
21941 {
21944 AttrMap *attmap;
21945 bool found;
21946 int i;
21950
21951 /*
21952 * If this partition already has an index attached, refuse the
21953 * operation.
21954 */
21956
21958 ereport(ERROR,
21960 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21962 RelationGetRelationName(parentIdx)),
21963 errdetail("Index \"%s\" is already attached to another index.",
21965
21966 /* Make sure it indexes a partition of the other index's table */
21967 partDesc = RelationGetPartitionDesc(parentTbl, true);
21968 found = false;
21969 for (i = 0; i < partDesc->nparts; i++)
21970 {
21971 if (partDesc->oids[i] == state.partitionOid)
21972 {
21973 found = true;
21974 break;
21975 }
21976 }
21977 if (!found)
21978 ereport(ERROR,
21980 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21982 RelationGetRelationName(parentIdx)),
21983 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21985 RelationGetRelationName(parentTbl))));
21986
21987 /* Ensure the indexes are compatible */
21989 parentInfo = BuildIndexInfo(parentIdx);
21991 RelationGetDescr(parentTbl),
21992 false);
21994 partIdx->rd_indcollation,
21995 parentIdx->rd_indcollation,
21996 partIdx->rd_opfamily,
21997 parentIdx->rd_opfamily,
21998 attmap))
21999 ereport(ERROR,
22001 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22003 RelationGetRelationName(parentIdx)),
22004 errdetail("The index definitions do not match.")));
22005
22006 /*
22007 * If there is a constraint in the parent, make sure there is one in
22008 * the child too.
22009 */
22011 RelationGetRelid(parentIdx));
22012
22014 {
22016 partIdxId);
22017 if (!OidIsValid(cldConstrId))
22018 ereport(ERROR,
22020 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22022 RelationGetRelationName(parentIdx)),
22023 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
22024 RelationGetRelationName(parentIdx),
22025 RelationGetRelationName(parentTbl),
22027 }
22028
22029 /*
22030 * If it's a primary key, make sure the columns in the partition are
22031 * NOT NULL.
22032 */
22033 if (parentIdx->rd_index->indisprimary)
22035
22036 /* All good -- do it */
22041
22043
22044 validatePartitionedIndex(parentIdx, parentTbl);
22045 }
22046 else if (!parentIdx->rd_index->indisvalid)
22047 {
22048 /*
22049 * The index is attached, but the parent is still invalid; see if it
22050 * can be validated now.
22051 */
22052 validatePartitionedIndex(parentIdx, parentTbl);
22053 }
22054
22055 relation_close(parentTbl, AccessShareLock);
22056 /* keep these locks till commit */
22059
22060 return address;
22061}
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:2555
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition indexcmds.c:4476
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 16328 of file tablecmds.c.

16329{
16332 HeapTuple tuple;
16334
16335 /*
16336 * Get exclusive lock till end of transaction on the target table. Use
16337 * relation_open so that we can work on indexes and sequences.
16338 */
16339 target_rel = relation_open(relationOid, lockmode);
16340
16341 /* Get its pg_class tuple, too */
16343
16344 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16345 if (!HeapTupleIsValid(tuple))
16346 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16348
16349 /* Can we change the ownership of this tuple? */
16350 switch (tuple_class->relkind)
16351 {
16352 case RELKIND_RELATION:
16353 case RELKIND_VIEW:
16354 case RELKIND_MATVIEW:
16357 case RELKIND_PROPGRAPH:
16358 /* ok to change owner */
16359 break;
16360 case RELKIND_INDEX:
16361 if (!recursing)
16362 {
16363 /*
16364 * Because ALTER INDEX OWNER used to be allowed, and in fact
16365 * is generated by old versions of pg_dump, we give a warning
16366 * and do nothing rather than erroring out. Also, to avoid
16367 * unnecessary chatter while restoring those old dumps, say
16368 * nothing at all if the command would be a no-op anyway.
16369 */
16370 if (tuple_class->relowner != newOwnerId)
16373 errmsg("cannot change owner of index \"%s\"",
16374 NameStr(tuple_class->relname)),
16375 errhint("Change the ownership of the index's table instead.")));
16376 /* quick hack to exit via the no-op path */
16377 newOwnerId = tuple_class->relowner;
16378 }
16379 break;
16381 if (recursing)
16382 break;
16383 ereport(ERROR,
16385 errmsg("cannot change owner of index \"%s\"",
16386 NameStr(tuple_class->relname)),
16387 errhint("Change the ownership of the index's table instead.")));
16388 break;
16389 case RELKIND_SEQUENCE:
16390 if (!recursing &&
16391 tuple_class->relowner != newOwnerId)
16392 {
16393 /* if it's an owned sequence, disallow changing it by itself */
16394 Oid tableId;
16395 int32 colId;
16396
16397 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16399 ereport(ERROR,
16401 errmsg("cannot change owner of sequence \"%s\"",
16402 NameStr(tuple_class->relname)),
16403 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16404 NameStr(tuple_class->relname),
16406 }
16407 break;
16409 if (recursing)
16410 break;
16411 ereport(ERROR,
16413 errmsg("\"%s\" is a composite type",
16414 NameStr(tuple_class->relname)),
16415 /* translator: %s is an SQL ALTER command */
16416 errhint("Use %s instead.",
16417 "ALTER TYPE")));
16418 break;
16419 case RELKIND_TOASTVALUE:
16420 if (recursing)
16421 break;
16423 default:
16424 ereport(ERROR,
16426 errmsg("cannot change owner of relation \"%s\"",
16427 NameStr(tuple_class->relname)),
16429 }
16430
16431 /*
16432 * If the new owner is the same as the existing owner, consider the
16433 * command to have succeeded. This is for dump restoration purposes.
16434 */
16435 if (tuple_class->relowner != newOwnerId)
16436 {
16440 Acl *newAcl;
16442 bool isNull;
16443 HeapTuple newtuple;
16444
16445 /* skip permission checks when recursing to index or toast table */
16446 if (!recursing)
16447 {
16448 /* Superusers can always do it */
16449 if (!superuser())
16450 {
16451 Oid namespaceOid = tuple_class->relnamespace;
16453
16454 /* Otherwise, must be owner of the existing object */
16455 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16458
16459 /* Must be able to become new owner */
16461
16462 /* New owner must have CREATE privilege on namespace */
16464 ACL_CREATE);
16465 if (aclresult != ACLCHECK_OK)
16468 }
16469 }
16470
16471 memset(repl_null, false, sizeof(repl_null));
16472 memset(repl_repl, false, sizeof(repl_repl));
16473
16476
16477 /*
16478 * Determine the modified ACL for the new owner. This is only
16479 * necessary when the ACL is non-null.
16480 */
16483 &isNull);
16484 if (!isNull)
16485 {
16487 tuple_class->relowner, newOwnerId);
16488 repl_repl[Anum_pg_class_relacl - 1] = true;
16490 }
16491
16493
16494 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16495
16496 heap_freetuple(newtuple);
16497
16498 /*
16499 * We must similarly update any per-column ACLs to reflect the new
16500 * owner; for neatness reasons that's split out as a subroutine.
16501 */
16502 change_owner_fix_column_acls(relationOid,
16503 tuple_class->relowner,
16504 newOwnerId);
16505
16506 /*
16507 * Update owner dependency reference, if any. A composite type has
16508 * none, because it's tracked for the pg_type entry instead of here;
16509 * indexes and TOAST tables don't have their own entries either.
16510 */
16511 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16512 tuple_class->relkind != RELKIND_INDEX &&
16514 tuple_class->relkind != RELKIND_TOASTVALUE)
16516 newOwnerId);
16517
16518 /*
16519 * Also change the ownership of the table's row type, if it has one
16520 */
16521 if (OidIsValid(tuple_class->reltype))
16523
16524 /*
16525 * If we are operating on a table or materialized view, also change
16526 * the ownership of any indexes and sequences that belong to the
16527 * relation, as well as its toast table (if it has one).
16528 */
16529 if (tuple_class->relkind == RELKIND_RELATION ||
16531 tuple_class->relkind == RELKIND_MATVIEW ||
16532 tuple_class->relkind == RELKIND_TOASTVALUE)
16533 {
16535 ListCell *i;
16536
16537 /* Find all the indexes belonging to this relation */
16539
16540 /* For each index, recursively change its ownership */
16541 foreach(i, index_oid_list)
16542 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16543
16545 }
16546
16547 /* If it has a toast table, recurse to change its ownership */
16548 if (tuple_class->reltoastrelid != InvalidOid)
16550 true, lockmode);
16551
16552 /* If it has dependent sequences, recurse to change them too */
16553 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16554 }
16555
16557
16558 ReleaseSysCache(tuple);
16561}
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition acl.c:1147
void check_can_set_role(Oid member, Oid role)
Definition acl.c:5371
#define DatumGetAclP(X)
Definition acl.h:120
#define pg_fallthrough
Definition c.h:161
#define WARNING
Definition elog.h:37
@ 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:4037

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

16705{
16706 Oid indexOid;
16707 ObjectAddress address;
16708
16709 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16710
16711 if (!OidIsValid(indexOid))
16712 ereport(ERROR,
16714 errmsg("index \"%s\" for table \"%s\" does not exist",
16716
16717 /* Check index is valid to cluster on */
16718 check_index_is_clusterable(rel, indexOid, lockmode);
16719
16720 /* And do the work */
16721 mark_index_clustered(rel, indexOid, false);
16722
16723 ObjectAddressSet(address,
16724 RelationRelationId, indexOid);
16725
16726 return address;
16727}
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition repack.c:769
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition repack.c:829

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

5447{
5449 Relation rel = tab->rel;
5450
5451 switch (cmd->subtype)
5452 {
5453 case AT_AddColumn: /* ADD COLUMN */
5454 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5455 address = ATExecAddColumn(wqueue, tab, rel, &cmd,
5456 cmd->recurse, false,
5457 lockmode, cur_pass, context);
5458 break;
5459 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5460 address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
5461 break;
5462 case AT_CookedColumnDefault: /* add a pre-cooked default */
5463 address = ATExecCookedColumnDefault(rel, cmd->num, cmd->def);
5464 break;
5465 case AT_AddIdentity:
5466 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5467 cur_pass, context);
5468 Assert(cmd != NULL);
5469 address = ATExecAddIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5470 break;
5471 case AT_SetIdentity:
5472 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5473 cur_pass, context);
5474 Assert(cmd != NULL);
5475 address = ATExecSetIdentity(rel, cmd->name, cmd->def, lockmode, cmd->recurse, false);
5476 break;
5477 case AT_DropIdentity:
5478 address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode, cmd->recurse, false);
5479 break;
5480 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5481 address = ATExecDropNotNull(rel, cmd->name, cmd->recurse, lockmode);
5482 break;
5483 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5484 address = ATExecSetNotNull(wqueue, rel, NULL, cmd->name,
5485 cmd->recurse, false, lockmode);
5486 break;
5487 case AT_SetExpression:
5488 address = ATExecSetExpression(tab, rel, cmd->name, cmd->def, lockmode);
5489 break;
5490 case AT_DropExpression:
5491 address = ATExecDropExpression(rel, cmd->name, cmd->missing_ok, lockmode);
5492 break;
5493 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5494 address = ATExecSetStatistics(rel, cmd->name, cmd->num, cmd->def, lockmode);
5495 break;
5496 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5497 address = ATExecSetOptions(rel, cmd->name, cmd->def, false, lockmode);
5498 break;
5499 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5500 address = ATExecSetOptions(rel, cmd->name, cmd->def, true, lockmode);
5501 break;
5502 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5503 address = ATExecSetStorage(rel, cmd->name, cmd->def, lockmode);
5504 break;
5505 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5506 address = ATExecSetCompression(rel, cmd->name, cmd->def,
5507 lockmode);
5508 break;
5509 case AT_DropColumn: /* DROP COLUMN */
5510 address = ATExecDropColumn(wqueue, rel, cmd->name,
5511 cmd->behavior, cmd->recurse, false,
5512 cmd->missing_ok, lockmode,
5513 NULL);
5514 break;
5515 case AT_AddIndex: /* ADD INDEX */
5516 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false,
5517 lockmode);
5518 break;
5519 case AT_ReAddIndex: /* ADD INDEX */
5520 address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
5521 lockmode);
5522 break;
5523 case AT_ReAddStatistics: /* ADD STATISTICS */
5524 address = ATExecAddStatistics(tab, rel, (CreateStatsStmt *) cmd->def,
5525 true, lockmode);
5526 break;
5527 case AT_AddConstraint: /* ADD CONSTRAINT */
5528 /* Transform the command only during initial examination */
5530 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd,
5531 cmd->recurse, lockmode,
5532 cur_pass, context);
5533 /* Depending on constraint type, might be no more work to do now */
5534 if (cmd != NULL)
5535 address =
5536 ATExecAddConstraint(wqueue, tab, rel,
5537 (Constraint *) cmd->def,
5538 cmd->recurse, false, lockmode);
5539 break;
5540 case AT_ReAddConstraint: /* Re-add pre-existing check constraint */
5541 address =
5542 ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
5543 true, true, lockmode);
5544 break;
5545 case AT_ReAddDomainConstraint: /* Re-add pre-existing domain check
5546 * constraint */
5547 address =
5548 AlterDomainAddConstraint(((AlterDomainStmt *) cmd->def)->typeName,
5549 ((AlterDomainStmt *) cmd->def)->def,
5550 NULL);
5551 break;
5552 case AT_ReAddComment: /* Re-add existing comment */
5553 address = CommentObject((CommentStmt *) cmd->def);
5554 break;
5555 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5556 address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
5557 lockmode);
5558 break;
5559 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5560 address = ATExecAlterConstraint(wqueue, rel,
5562 cmd->recurse, lockmode);
5563 break;
5564 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5565 address = ATExecValidateConstraint(wqueue, rel, cmd->name, cmd->recurse,
5566 false, lockmode);
5567 break;
5568 case AT_DropConstraint: /* DROP CONSTRAINT */
5569 ATExecDropConstraint(rel, cmd->name, cmd->behavior,
5570 cmd->recurse,
5571 cmd->missing_ok, lockmode);
5572 break;
5573 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5574 /* parse transformation was done earlier */
5575 address = ATExecAlterColumnType(tab, rel, cmd, lockmode);
5576 break;
5577 case AT_AlterColumnGenericOptions: /* ALTER COLUMN OPTIONS */
5578 address =
5580 (List *) cmd->def, lockmode);
5581 break;
5582 case AT_ChangeOwner: /* ALTER OWNER */
5584 get_rolespec_oid(cmd->newowner, false),
5585 false, lockmode);
5586 break;
5587 case AT_ClusterOn: /* CLUSTER ON */
5588 address = ATExecClusterOn(rel, cmd->name, lockmode);
5589 break;
5590 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5591 ATExecDropCluster(rel, lockmode);
5592 break;
5593 case AT_SetLogged: /* SET LOGGED */
5594 case AT_SetUnLogged: /* SET UNLOGGED */
5595 break;
5596 case AT_DropOids: /* SET WITHOUT OIDS */
5597 /* nothing to do here, oid columns don't exist anymore */
5598 break;
5599 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5600
5601 /*
5602 * Only do this for partitioned tables, for which this is just a
5603 * catalog change. Tables with storage are handled by Phase 3.
5604 */
5605 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE &&
5606 tab->chgAccessMethod)
5608 break;
5609 case AT_SetTableSpace: /* SET TABLESPACE */
5610
5611 /*
5612 * Only do this for partitioned tables and indexes, for which this
5613 * is just a catalog change. Other relation types which have
5614 * storage are handled by Phase 3.
5615 */
5616 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
5617 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
5619
5620 break;
5621 case AT_SetRelOptions: /* SET (...) */
5622 case AT_ResetRelOptions: /* RESET (...) */
5623 case AT_ReplaceRelOptions: /* replace entire option list */
5624 ATExecSetRelOptions(rel, (List *) cmd->def, cmd->subtype, lockmode);
5625 break;
5626 case AT_EnableTrig: /* ENABLE TRIGGER name */
5629 cmd->recurse,
5630 lockmode);
5631 break;
5632 case AT_EnableAlwaysTrig: /* ENABLE ALWAYS TRIGGER name */
5634 TRIGGER_FIRES_ALWAYS, false,
5635 cmd->recurse,
5636 lockmode);
5637 break;
5638 case AT_EnableReplicaTrig: /* ENABLE REPLICA TRIGGER name */
5641 cmd->recurse,
5642 lockmode);
5643 break;
5644 case AT_DisableTrig: /* DISABLE TRIGGER name */
5646 TRIGGER_DISABLED, false,
5647 cmd->recurse,
5648 lockmode);
5649 break;
5650 case AT_EnableTrigAll: /* ENABLE TRIGGER ALL */
5653 cmd->recurse,
5654 lockmode);
5655 break;
5656 case AT_DisableTrigAll: /* DISABLE TRIGGER ALL */
5658 TRIGGER_DISABLED, false,
5659 cmd->recurse,
5660 lockmode);
5661 break;
5662 case AT_EnableTrigUser: /* ENABLE TRIGGER USER */
5665 cmd->recurse,
5666 lockmode);
5667 break;
5668 case AT_DisableTrigUser: /* DISABLE TRIGGER USER */
5670 TRIGGER_DISABLED, true,
5671 cmd->recurse,
5672 lockmode);
5673 break;
5674
5675 case AT_EnableRule: /* ENABLE RULE name */
5676 ATExecEnableDisableRule(rel, cmd->name,
5677 RULE_FIRES_ON_ORIGIN, lockmode);
5678 break;
5679 case AT_EnableAlwaysRule: /* ENABLE ALWAYS RULE name */
5680 ATExecEnableDisableRule(rel, cmd->name,
5681 RULE_FIRES_ALWAYS, lockmode);
5682 break;
5683 case AT_EnableReplicaRule: /* ENABLE REPLICA RULE name */
5684 ATExecEnableDisableRule(rel, cmd->name,
5685 RULE_FIRES_ON_REPLICA, lockmode);
5686 break;
5687 case AT_DisableRule: /* DISABLE RULE name */
5688 ATExecEnableDisableRule(rel, cmd->name,
5689 RULE_DISABLED, lockmode);
5690 break;
5691
5692 case AT_AddInherit:
5693 address = ATExecAddInherit(rel, (RangeVar *) cmd->def, lockmode);
5694 break;
5695 case AT_DropInherit:
5696 address = ATExecDropInherit(rel, (RangeVar *) cmd->def, lockmode);
5697 break;
5698 case AT_AddOf:
5699 address = ATExecAddOf(rel, (TypeName *) cmd->def, lockmode);
5700 break;
5701 case AT_DropOf:
5702 ATExecDropOf(rel, lockmode);
5703 break;
5704 case AT_ReplicaIdentity:
5705 ATExecReplicaIdentity(rel, (ReplicaIdentityStmt *) cmd->def, lockmode);
5706 break;
5708 ATExecSetRowSecurity(rel, true);
5709 break;
5711 ATExecSetRowSecurity(rel, false);
5712 break;
5715 break;
5718 break;
5719 case AT_GenericOptions:
5720 ATExecGenericOptions(rel, (List *) cmd->def);
5721 break;
5722 case AT_AttachPartition:
5723 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5724 cur_pass, context);
5725 Assert(cmd != NULL);
5726 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
5727 address = ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def,
5728 context);
5729 else
5730 address = ATExecAttachPartitionIdx(wqueue, rel,
5731 ((PartitionCmd *) cmd->def)->name);
5732 break;
5733 case AT_DetachPartition:
5734 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5735 cur_pass, context);
5736 Assert(cmd != NULL);
5737 /* ATPrepCmd ensures it must be a table */
5738 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5739 address = ATExecDetachPartition(wqueue, tab, rel,
5740 ((PartitionCmd *) cmd->def)->name,
5741 ((PartitionCmd *) cmd->def)->concurrent);
5742 break;
5744 address = ATExecDetachPartitionFinalize(rel, ((PartitionCmd *) cmd->def)->name);
5745 break;
5746 case AT_MergePartitions:
5747 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5748 cur_pass, context);
5749 Assert(cmd != NULL);
5750 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5751 ATExecMergePartitions(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5752 context);
5753 break;
5754 case AT_SplitPartition:
5755 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, false, lockmode,
5756 cur_pass, context);
5757 Assert(cmd != NULL);
5758 Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
5759 ATExecSplitPartition(wqueue, tab, rel, (PartitionCmd *) cmd->def,
5760 context);
5761 break;
5762 default: /* oops */
5763 elog(ERROR, "unrecognized alter table type: %d",
5764 (int) cmd->subtype);
5765 break;
5766 }
5767
5768 /*
5769 * Report the subcommand to interested event triggers.
5770 */
5771 if (cmd)
5772 EventTriggerCollectAlterTableSubcmd((Node *) cmd, address);
5773
5774 /*
5775 * Bump the command counter to ensure the next subcommand in the sequence
5776 * can see the changes so far
5777 */
5779}
Oid get_rolespec_oid(const RoleSpec *role, bool missing_ok)
Definition acl.c:5639
ObjectAddress CommentObject(CommentStmt *stmt)
Definition comment.c:40
void EventTriggerCollectAlterTableSubcmd(const 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:8879
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition tablecmds.c:8216
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition tablecmds.c:8578
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:7832
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition tablecmds.c:9874
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:9128
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:9361
static void ATExecSetRowSecurity(Relation rel, bool rls)
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition tablecmds.c:8301
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:8461
static void ATExecGenericOptions(Relation rel, List *options)
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition tablecmds.c:8983
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:8692
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:9782
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:9270
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:9697
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:9761
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:2967

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

8218{
8219 TupleDesc tupdesc = RelationGetDescr(rel);
8221 ObjectAddress address;
8222
8223 /*
8224 * get the number of the attribute
8225 */
8228 ereport(ERROR,
8230 errmsg("column \"%s\" of relation \"%s\" does not exist",
8232
8233 /* Prevent them from altering a system attribute */
8234 if (attnum <= 0)
8235 ereport(ERROR,
8237 errmsg("cannot alter system column \"%s\"",
8238 colName)));
8239
8240 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8241 ereport(ERROR,
8243 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8245 /* translator: %s is an SQL ALTER command */
8246 newDefault ? 0 : errhint("Use %s instead.",
8247 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8248
8249 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8250 ereport(ERROR,
8252 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8254 newDefault ?
8255 /* translator: %s is an SQL ALTER command */
8256 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8257 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8258 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8259
8260 /*
8261 * Remove any old default for the column. We use RESTRICT here for
8262 * safety, but at present we do not expect anything to depend on the
8263 * default.
8264 *
8265 * We treat removing the existing default as an internal operation when it
8266 * is preparatory to adding a new default, but as a user-initiated
8267 * operation when the user asked for a drop.
8268 */
8270 newDefault != NULL);
8271
8272 if (newDefault)
8273 {
8274 /* SET DEFAULT */
8276
8278 rawEnt->attnum = attnum;
8279 rawEnt->raw_default = newDefault;
8280 rawEnt->generated = '\0';
8281
8282 /*
8283 * This function is intended for CREATE TABLE, so it processes a
8284 * _list_ of defaults, but we just do one.
8285 */
8287 false, true, false, NULL);
8288 }
8289
8291 RelationGetRelid(rel), attnum);
8292 return address;
8293}
#define InvalidAttrNumber
Definition attnum.h:23
AttrNumber get_attnum(Oid relid, const char *attname)
Definition lsyscache.c:1076

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

8303{
8304 ObjectAddress address;
8305
8306 /* We assume no checking is required */
8307
8308 /*
8309 * Remove any old default for the column. We use RESTRICT here for
8310 * safety, but at present we do not expect anything to depend on the
8311 * default. (In ordinary cases, there could not be a default in place
8312 * anyway, but it's possible when combining LIKE with inheritance.)
8313 */
8315 true);
8316
8317 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8318
8320 RelationGetRelid(rel), attnum);
8321 return address;
8322}

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

21229{
21230 Relation partRel;
21231 ObjectAddress address;
21233
21234 /*
21235 * We must lock the default partition, because detaching this partition
21236 * will change its partition constraint.
21237 */
21241 {
21242 /*
21243 * Concurrent detaching when a default partition exists is not
21244 * supported. The main problem is that the default partition
21245 * constraint would change. And there's a definitional problem: what
21246 * should happen to the tuples that are being inserted that belong to
21247 * the partition being detached? Putting them on the partition being
21248 * detached would be wrong, since they'd become "lost" after the
21249 * detaching completes but we cannot put them in the default partition
21250 * either until we alter its partition constraint.
21251 *
21252 * I think we could solve this problem if we effected the constraint
21253 * change before committing the first transaction. But the lock would
21254 * have to remain AEL and it would cause concurrent query planning to
21255 * be blocked, so changing it that way would be even worse.
21256 */
21257 if (concurrent)
21258 ereport(ERROR,
21260 errmsg("cannot detach partitions concurrently when a default partition exists")));
21262 }
21263
21264 /*
21265 * In concurrent mode, the partition is locked with share-update-exclusive
21266 * in the first transaction. This allows concurrent transactions to be
21267 * doing DML to the partition.
21268 */
21269 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
21271
21272 /*
21273 * Check inheritance conditions and either delete the pg_inherits row (in
21274 * non-concurrent mode) or just set the inhdetachpending flag.
21275 */
21276 if (!concurrent)
21277 RemoveInheritance(partRel, rel, false);
21278 else
21279 MarkInheritDetached(partRel, rel);
21280
21281 /*
21282 * Ensure that foreign keys still hold after this detach. This keeps
21283 * locks on the referencing tables, which prevents concurrent transactions
21284 * from adding rows that we wouldn't see. For this to work in concurrent
21285 * mode, it is critical that the partition appears as no longer attached
21286 * for the RI queries as soon as the first transaction commits.
21287 */
21289
21290 /*
21291 * Concurrent mode has to work harder; first we add a new constraint to
21292 * the partition that matches the partition constraint. Then we close our
21293 * existing transaction, and in a new one wait for all processes to catch
21294 * up on the catalog updates we've done so far; at that point we can
21295 * complete the operation.
21296 */
21297 if (concurrent)
21298 {
21299 Oid partrelid,
21301 LOCKTAG tag;
21302 char *parentrelname;
21303 char *partrelname;
21304
21305 /*
21306 * We're almost done now; the only traces that remain are the
21307 * pg_inherits tuple and the partition's relpartbounds. Before we can
21308 * remove those, we need to wait until all transactions that know that
21309 * this is a partition are gone.
21310 */
21311
21312 /*
21313 * Remember relation OIDs to re-acquire them later; and relation names
21314 * too, for error messages if something is dropped in between.
21315 */
21316 partrelid = RelationGetRelid(partRel);
21321 RelationGetRelationName(partRel));
21322
21323 /* Invalidate relcache entries for the parent -- must be before close */
21325
21326 table_close(partRel, NoLock);
21327 table_close(rel, NoLock);
21328 tab->rel = NULL;
21329
21330 /* Make updated catalog entry visible */
21333
21335
21336 /*
21337 * Now wait. This ensures that all queries that were planned
21338 * including the partition are finished before we remove the rest of
21339 * catalog entries. We don't need or indeed want to acquire this
21340 * lock, though -- that would block later queries.
21341 *
21342 * We don't need to concern ourselves with waiting for a lock on the
21343 * partition itself, since we will acquire AccessExclusiveLock below.
21344 */
21347
21348 /*
21349 * Now acquire locks in both relations again. Note they may have been
21350 * removed in the meantime, so care is required.
21351 */
21354
21355 /* If the relations aren't there, something bad happened; bail out */
21356 if (rel == NULL)
21357 {
21358 if (partRel != NULL) /* shouldn't happen */
21359 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21360 partrelname);
21361 ereport(ERROR,
21363 errmsg("partitioned table \"%s\" was removed concurrently",
21364 parentrelname)));
21365 }
21366 if (partRel == NULL)
21367 ereport(ERROR,
21369 errmsg("partition \"%s\" was removed concurrently", partrelname)));
21370
21371 tab->rel = rel;
21372 }
21373
21374 /*
21375 * Detaching the partition might involve TOAST table access, so ensure we
21376 * have a valid snapshot.
21377 */
21379
21380 /* Do the final part of detaching */
21381 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21382
21384
21386
21387 /* keep our lock until commit */
21388 table_close(partRel, NoLock);
21389
21390 return address;
21391}
Oid MyDatabaseId
Definition globals.c:96
void CacheInvalidateRelcache(Relation relation)
Definition inval.c:1632
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition lmgr.c:911
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition locktag.h:81
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition mcxt.c:1897
MemoryContext PortalContext
Definition mcxt.c:176
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:89
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:3109
void CommitTransactionCommand(void)
Definition xact.c:3207

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

21735{
21736 Relation partRel;
21737 ObjectAddress address;
21739
21741
21742 /*
21743 * Wait until existing snapshots are gone. This is important if the
21744 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21745 * user could immediately run DETACH FINALIZE without actually waiting for
21746 * existing transactions. We must not complete the detach action until
21747 * all such queries are complete (otherwise we would present them with an
21748 * inconsistent view of catalogs).
21749 */
21750 WaitForOlderSnapshots(snap->xmin, false);
21751
21752 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21753
21755
21756 table_close(partRel, NoLock);
21757
21758 return address;
21759}
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition indexcmds.c:436
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 16736 of file tablecmds.c.

16737{
16738 mark_index_clustered(rel, InvalidOid, false);
16739}

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

9366{
9367 HeapTuple tuple;
9370 List *children;
9371 ObjectAddress object;
9372 bool is_expr;
9373
9374 /* At top level, permission check was done in ATPrepCmd, else do it */
9375 if (recursing)
9378
9379 /* Initialize addrs on the first invocation */
9380 Assert(!recursing || addrs != NULL);
9381
9382 /* since this function recurses, it could be driven to stack overflow */
9384
9385 if (!recursing)
9386 addrs = new_object_addresses();
9387
9388 /*
9389 * get the number of the attribute
9390 */
9392 if (!HeapTupleIsValid(tuple))
9393 {
9394 if (!missing_ok)
9395 {
9396 ereport(ERROR,
9398 errmsg("column \"%s\" of relation \"%s\" does not exist",
9400 }
9401 else
9402 {
9404 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9406 return InvalidObjectAddress;
9407 }
9408 }
9410
9411 attnum = targetatt->attnum;
9412
9413 /* Can't drop a system attribute */
9414 if (attnum <= 0)
9415 ereport(ERROR,
9417 errmsg("cannot drop system column \"%s\"",
9418 colName)));
9419
9420 /*
9421 * Don't drop inherited columns, unless recursing (presumably from a drop
9422 * of the parent column)
9423 */
9424 if (targetatt->attinhcount > 0 && !recursing)
9425 ereport(ERROR,
9427 errmsg("cannot drop inherited column \"%s\"",
9428 colName)));
9429
9430 /*
9431 * Don't drop columns used in the partition key, either. (If we let this
9432 * go through, the key column's dependencies would cause a cascaded drop
9433 * of the whole table, which is surely not what the user expected.)
9434 */
9435 if (has_partition_attrs(rel,
9437 &is_expr))
9438 ereport(ERROR,
9440 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9442
9443 ReleaseSysCache(tuple);
9444
9445 /*
9446 * Propagate to children as appropriate. Unlike most other ALTER
9447 * routines, we have to do this one level of recursion at a time; we can't
9448 * use find_all_inheritors to do it in one pass.
9449 */
9450 children =
9452
9453 if (children)
9454 {
9456 ListCell *child;
9457
9458 /*
9459 * In case of a partitioned table, the column must be dropped from the
9460 * partitions as well.
9461 */
9462 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9463 ereport(ERROR,
9465 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9466 errhint("Do not specify the ONLY keyword.")));
9467
9469 foreach(child, children)
9470 {
9471 Oid childrelid = lfirst_oid(child);
9474
9475 /* find_inheritance_children already got lock */
9478
9480 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9481 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9484
9485 if (childatt->attinhcount <= 0) /* shouldn't happen */
9486 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9488
9489 if (recurse)
9490 {
9491 /*
9492 * If the child column has other definition sources, just
9493 * decrement its inheritance count; if not, recurse to delete
9494 * it.
9495 */
9496 if (childatt->attinhcount == 1 && !childatt->attislocal)
9497 {
9498 /* Time to delete this child column, too */
9500 behavior, true, true,
9501 false, lockmode, addrs);
9502 }
9503 else
9504 {
9505 /* Child column must survive my deletion */
9506 childatt->attinhcount--;
9507
9508 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9509
9510 /* Make update visible */
9512 }
9513 }
9514 else
9515 {
9516 /*
9517 * If we were told to drop ONLY in this table (no recursion),
9518 * we need to mark the inheritors' attributes as locally
9519 * defined rather than inherited.
9520 */
9521 childatt->attinhcount--;
9522 childatt->attislocal = true;
9523
9524 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9525
9526 /* Make update visible */
9528 }
9529
9530 heap_freetuple(tuple);
9531
9533 }
9535 }
9536
9537 /* Add object to delete */
9538 object.classId = RelationRelationId;
9539 object.objectId = RelationGetRelid(rel);
9540 object.objectSubId = attnum;
9541 add_exact_object_address(&object, addrs);
9542
9543 if (!recursing)
9544 {
9545 /* Recursion has ended, drop everything that was collected */
9546 performMultipleDeletions(addrs, behavior, 0);
9547 free_object_addresses(addrs);
9548 }
9549
9550 return object;
9551}
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:216
void performMultipleDeletions(const ObjectAddresses *objects, DropBehavior behavior, int flags)
Definition dependency.c:388
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 14268 of file tablecmds.c.

14271{
14273 SysScanDesc scan;
14274 ScanKeyData skey[3];
14275 HeapTuple tuple;
14276 bool found = false;
14277
14279
14280 /*
14281 * Find and drop the target constraint
14282 */
14283 ScanKeyInit(&skey[0],
14287 ScanKeyInit(&skey[1],
14291 ScanKeyInit(&skey[2],
14296 true, NULL, 3, skey);
14297
14298 /* There can be at most one matching row */
14299 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14300 {
14301 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14302 missing_ok, lockmode);
14303 found = true;
14304 }
14305
14306 systable_endscan(scan);
14307
14308 if (!found)
14309 {
14310 if (!missing_ok)
14311 ereport(ERROR,
14313 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14315 else
14317 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14319 }
14320
14322}
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 8879 of file tablecmds.c.

8880{
8881 HeapTuple tuple;
8886 ObjectAddress address;
8887
8890 if (!HeapTupleIsValid(tuple))
8891 ereport(ERROR,
8893 errmsg("column \"%s\" of relation \"%s\" does not exist",
8895
8897 attnum = attTup->attnum;
8898
8899 if (attnum <= 0)
8900 ereport(ERROR,
8902 errmsg("cannot alter system column \"%s\"",
8903 colName)));
8904
8905 /*
8906 * TODO: This could be done, but it would need a table rewrite to
8907 * materialize the generated values. Note that for the time being, we
8908 * still error with missing_ok, so that we don't silently leave the column
8909 * as generated.
8910 */
8911 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8912 ereport(ERROR,
8914 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8915 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8917
8918 if (!attTup->attgenerated)
8919 {
8920 if (!missing_ok)
8921 ereport(ERROR,
8923 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8925 else
8926 {
8928 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8930 heap_freetuple(tuple);
8932 return InvalidObjectAddress;
8933 }
8934 }
8935
8936 /*
8937 * Mark the column as no longer generated. (The atthasdef flag needs to
8938 * get cleared too, but RemoveAttrDefault will handle that.)
8939 */
8940 attTup->attgenerated = '\0';
8941 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8942
8944 RelationGetRelid(rel),
8945 attnum);
8946 heap_freetuple(tuple);
8947
8949
8950 /*
8951 * Drop the dependency records of the GENERATED expression, in particular
8952 * its INTERNAL dependency on the column, which would otherwise cause
8953 * dependency.c to refuse to perform the deletion.
8954 */
8956 if (!OidIsValid(attrdefoid))
8957 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8958 RelationGetRelid(rel), attnum);
8960
8961 /* Make above changes visible */
8963
8964 /*
8965 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8966 * safety, but at present we do not expect anything to depend on the
8967 * default.
8968 */
8970 false, false);
8971
8973 RelationGetRelid(rel), attnum);
8974 return address;
8975}

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

8580{
8581 HeapTuple tuple;
8585 ObjectAddress address;
8586 Oid seqid;
8588 bool ispartitioned;
8589
8590 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8591 if (ispartitioned && !recurse)
8592 ereport(ERROR,
8594 errmsg("cannot drop identity from a column of only the partitioned table"),
8595 errhint("Do not specify the ONLY keyword.")));
8596
8597 if (rel->rd_rel->relispartition && !recursing)
8598 ereport(ERROR,
8600 errmsg("cannot drop identity from a column of a partition"));
8601
8604 if (!HeapTupleIsValid(tuple))
8605 ereport(ERROR,
8607 errmsg("column \"%s\" of relation \"%s\" does not exist",
8609
8611 attnum = attTup->attnum;
8612
8613 if (attnum <= 0)
8614 ereport(ERROR,
8616 errmsg("cannot alter system column \"%s\"",
8617 colName)));
8618
8619 if (!attTup->attidentity)
8620 {
8621 if (!missing_ok)
8622 ereport(ERROR,
8624 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8626 else
8627 {
8629 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8631 heap_freetuple(tuple);
8633 return InvalidObjectAddress;
8634 }
8635 }
8636
8637 attTup->attidentity = '\0';
8638 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8639
8641 RelationGetRelid(rel),
8642 attTup->attnum);
8644 RelationGetRelid(rel), attnum);
8645 heap_freetuple(tuple);
8646
8648
8649 /*
8650 * Recurse to drop the identity from column in partitions. Identity is
8651 * not inherited in regular inheritance children so ignore them.
8652 */
8653 if (recurse && ispartitioned)
8654 {
8655 List *children;
8656 ListCell *lc;
8657
8658 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8659
8660 foreach(lc, children)
8661 {
8663
8665 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8667 }
8668 }
8669
8670 if (!recursing)
8671 {
8672 /* drop the internal sequence */
8673 seqid = getIdentitySequence(rel, attnum, false);
8678 seqaddress.objectId = seqid;
8679 seqaddress.objectSubId = 0;
8681 }
8682
8683 return address;
8684}
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition dependency.c:279
#define PERFORM_DELETION_INTERNAL
Definition dependency.h:92
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:364
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition pg_depend.c:1149

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

18079{
18080 ObjectAddress address;
18082
18083 /*
18084 * AccessShareLock on the parent is probably enough, seeing that DROP
18085 * TABLE doesn't lock parent tables at all. We need some lock since we'll
18086 * be inspecting the parent's schema.
18087 */
18089
18090 /*
18091 * We don't bother to check ownership of the parent table --- ownership of
18092 * the child is presumed enough rights.
18093 */
18094
18095 /* Off to RemoveInheritance() where most of the work happens */
18096 RemoveInheritance(rel, parent_rel, false);
18097
18100
18101 /* keep our lock on the parent relation until commit */
18103
18104 return address;
18105}

References AccessShareLock, fb(), NoLock, ObjectAddressSet, 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 7832 of file tablecmds.c.

7834{
7835 HeapTuple tuple;
7840 ObjectAddress address;
7841
7842 /*
7843 * lookup the attribute
7844 */
7846
7848 if (!HeapTupleIsValid(tuple))
7849 ereport(ERROR,
7851 errmsg("column \"%s\" of relation \"%s\" does not exist",
7854 attnum = attTup->attnum;
7856 RelationGetRelid(rel), attnum);
7857
7858 /* If the column is already nullable there's nothing to do. */
7859 if (!attTup->attnotnull)
7860 {
7862 return InvalidObjectAddress;
7863 }
7864
7865 /* Prevent them from altering a system attribute */
7866 if (attnum <= 0)
7867 ereport(ERROR,
7869 errmsg("cannot alter system column \"%s\"",
7870 colName)));
7871
7872 if (attTup->attidentity)
7873 ereport(ERROR,
7875 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7877
7878 /*
7879 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7880 */
7881 if (rel->rd_rel->relispartition)
7882 {
7885 TupleDesc tupDesc = RelationGetDescr(parent);
7887
7889 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7890 ereport(ERROR,
7892 errmsg("column \"%s\" is marked NOT NULL in parent table",
7893 colName)));
7895 }
7896
7897 /*
7898 * Find the constraint that makes this column NOT NULL, and drop it.
7899 * dropconstraint_internal() resets attnotnull.
7900 */
7902 if (conTup == NULL)
7903 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7905
7906 /* The normal case: we have a pg_constraint row, remove it */
7907 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7908 false, lockmode);
7910
7912 RelationGetRelid(rel), attnum);
7913
7915
7916 return address;
7917}
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 18606 of file tablecmds.c.

18607{
18608 Oid relid = RelationGetRelid(rel);
18610 HeapTuple tuple;
18611
18612 if (!OidIsValid(rel->rd_rel->reloftype))
18613 ereport(ERROR,
18615 errmsg("\"%s\" is not a typed table",
18617
18618 /*
18619 * We don't bother to check ownership of the type --- ownership of the
18620 * table is presumed enough rights. No lock required on the type, either.
18621 */
18622
18623 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18625
18626 /* Clear pg_class.reloftype */
18629 if (!HeapTupleIsValid(tuple))
18630 elog(ERROR, "cache lookup failed for relation %u", relid);
18631 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18633
18635
18636 heap_freetuple(tuple);
18638}

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

17481{
17482 EnableDisableRule(rel, rulename, fires_when);
17483
17485 RelationGetRelid(rel), 0);
17486}
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 17461 of file tablecmds.c.

17464{
17465 EnableDisableTrigger(rel, trigname, InvalidOid,
17466 fires_when, skip_system, recurse,
17467 lockmode);
17468
17470 RelationGetRelid(rel), 0);
17471}
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition trigger.c:1728

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

Referenced by ATExecCmd().

◆ ATExecForceNoForceRowSecurity()

static void ATExecForceNoForceRowSecurity ( Relation  rel,
bool  force_rls 
)
static

Definition at line 18882 of file tablecmds.c.

18883{
18885 Oid relid;
18886 HeapTuple tuple;
18887
18888 relid = RelationGetRelid(rel);
18889
18891
18893
18894 if (!HeapTupleIsValid(tuple))
18895 elog(ERROR, "cache lookup failed for relation %u", relid);
18896
18898 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18899
18901 RelationGetRelid(rel), 0);
18902
18904 heap_freetuple(tuple);
18905}

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

18912{
18914 ForeignServer *server;
18916 HeapTuple tuple;
18917 bool isnull;
18921 Datum datum;
18923
18924 if (options == NIL)
18925 return;
18926
18928
18930 ObjectIdGetDatum(rel->rd_id));
18931 if (!HeapTupleIsValid(tuple))
18932 ereport(ERROR,
18934 errmsg("foreign table \"%s\" does not exist",
18937 server = GetForeignServer(tableform->ftserver);
18938 fdw = GetForeignDataWrapper(server->fdwid);
18939
18940 memset(repl_val, 0, sizeof(repl_val));
18941 memset(repl_null, false, sizeof(repl_null));
18942 memset(repl_repl, false, sizeof(repl_repl));
18943
18944 /* Extract the current options */
18946 tuple,
18948 &isnull);
18949 if (isnull)
18950 datum = PointerGetDatum(NULL);
18951
18952 /* Transform the options */
18954 datum,
18955 options,
18956 fdw->fdwvalidator);
18957
18958 if (DatumGetPointer(datum) != NULL)
18960 else
18962
18964
18965 /* Everything looks good - update the tuple */
18966
18969
18970 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18971
18972 /*
18973 * Invalidate relcache so that all sessions will refresh any cached plans
18974 * that might depend on the old options.
18975 */
18977
18979 RelationGetRelid(rel), 0);
18980
18982
18983 heap_freetuple(tuple);
18984}

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

23248{
23251 List *extDepState = NIL;
23254 Oid ownerId = InvalidOid;
23255 Oid save_userid;
23256 int save_sec_context;
23257 int save_nestlevel;
23258
23259 /*
23260 * Check ownership of merged partitions - partitions with different owners
23261 * cannot be merged. Also, collect the OIDs of these partitions during the
23262 * check.
23263 */
23265 {
23267
23268 /*
23269 * We are going to detach and remove this partition. We already took
23270 * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
23271 * NoLock is fine.
23272 */
23275
23276 if (OidIsValid(ownerId))
23277 {
23278 /* Do the partitions being merged have different owners? */
23279 if (ownerId != mergingPartition->rd_rel->relowner)
23280 ereport(ERROR,
23282 errmsg("partitions being merged have different owners"));
23283 }
23284 else
23285 ownerId = mergingPartition->rd_rel->relowner;
23286
23287 /* Store the next merging partition into the list. */
23290
23292 }
23293
23294 /* Look up the existing relation by the new partition name. */
23296
23297 /*
23298 * Check if this name is already taken. This helps us to detect the
23299 * situation when one of the merging partitions has the same name as the
23300 * new partition. Otherwise, this would fail later on anyway, but
23301 * catching this here allows us to emit a nicer error message.
23302 */
23304 {
23306 {
23307 /*
23308 * The new partition has the same name as one of the merging
23309 * partitions.
23310 */
23311 char tmpRelName[NAMEDATALEN];
23312
23313 /* Generate a temporary name. */
23314 sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23315
23316 /*
23317 * Rename the existing partition with a temporary name, leaving it
23318 * free for the new partition. We don't need to care about this
23319 * in the future because we're going to eventually drop the
23320 * existing partition anyway.
23321 */
23323
23324 /*
23325 * We must bump the command counter to make the new partition
23326 * tuple visible for rename.
23327 */
23329 }
23330 else
23331 {
23332 ereport(ERROR,
23334 errmsg("relation \"%s\" already exists", cmd->name->relname));
23335 }
23336 }
23337
23340
23341 /*
23342 * Collect extension dependencies from indexes on the merging partitions.
23343 * We must do this before detaching them, so we can restore the
23344 * dependencies on the new partition's indexes later.
23345 */
23347
23348 /* Detach all merging partitions. */
23350 {
23352
23354
23356
23358 }
23359
23360 /*
23361 * Perform a preliminary check to determine whether it's safe to drop all
23362 * merging partitions before we actually do so later. After merging rows
23363 * into the new partitions via MergePartitionsMoveRows, all old partitions
23364 * need to be dropped. However, since the drop behavior is DROP_RESTRICT
23365 * and the merge process (MergePartitionsMoveRows) can be time-consuming,
23366 * performing an early check on the drop eligibility of old partitions is
23367 * preferable.
23368 */
23370 {
23371 ObjectAddress object;
23372
23373 /* Get oid of the later to be dropped relation. */
23375 object.classId = RelationRelationId;
23376 object.objectSubId = 0;
23377
23379 }
23380
23381 /*
23382 * Create a table for the new partition, using the partitioned table as a
23383 * model.
23384 */
23385 Assert(OidIsValid(ownerId));
23386 newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
23387
23388 /*
23389 * Switch to the table owner's userid, so that any index functions are run
23390 * as that user. Also, lockdown security-restricted operations and
23391 * arrange to make GUC variable changes local to this command.
23392 *
23393 * Need to do it after determining the namespace in the
23394 * createPartitionTable() call.
23395 */
23396 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23397 SetUserIdAndSecContext(ownerId,
23398 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23399 save_nestlevel = NewGUCNestLevel();
23401
23402 /* Copy data from merged partitions to the new partition. */
23404
23405 /* Drop the current partitions before attaching the new one. */
23407 {
23408 ObjectAddress object;
23409
23411 object.classId = RelationRelationId;
23412 object.objectSubId = 0;
23413
23414 performDeletion(&object, DROP_RESTRICT, 0);
23415 }
23416
23418
23419 /*
23420 * Attach a new partition to the partitioned table. wqueue = NULL:
23421 * verification for each cloned constraint is not needed.
23422 */
23424
23425 /*
23426 * Apply extension dependencies to the new partition's indexes. This
23427 * preserves any "DEPENDS ON EXTENSION" settings from the merged
23428 * partitions.
23429 */
23431
23433
23434 /* Keep the lock until commit. */
23436
23437 /* Roll back any GUC changes executed by index functions. */
23438 AtEOXact_GUC(false, save_nestlevel);
23439
23440 /* Restore the userid and security context. */
23441 SetUserIdAndSecContext(save_userid, save_sec_context);
23442}
void performDeletionCheck(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition dependency.c:338
int MyProcPid
Definition globals.c:49
int NewGUCNestLevel(void)
Definition guc.c:2142
void RestrictSearchPath(void)
Definition guc.c:2153
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition guc.c:2169
#define SECURITY_RESTRICTED_OPERATION
Definition miscadmin.h:322
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition miscinit.c:613
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition miscinit.c:620
#define sprintf
Definition port.h:263
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 void applyPartitionIndexExtDeps(Oid newPartOid, List *extDepState)
static Relation createPartitionTable(List **wqueue, RangeVar *newPartName, Relation parent_rel, Oid ownerId)
static void detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
static List * collectPartitionIndexExtDeps(List *partitionOids)
static void freePartitionIndexExtDeps(List *extDepState)

References AccessExclusiveLock, applyPartitionIndexExtDeps(), Assert, AtEOXact_GUC(), attachPartitionTable(), PartitionCmd::bound, CheckRelationLockedByMe(), collectPartitionIndexExtDeps(), CommandCounterIncrement(), createPartitionTable(), detachPartitionTable(), DROP_RESTRICT, ereport, errcode(), errmsg, ERROR, fb(), foreach_node, foreach_oid, freePartitionIndexExtDeps(), 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 18738 of file tablecmds.c.

18739{
18740 Oid indexOid;
18741 Relation indexRel;
18742 int key;
18743
18744 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18745 {
18746 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18747 return;
18748 }
18749 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18750 {
18751 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18752 return;
18753 }
18754 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18755 {
18756 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18757 return;
18758 }
18759 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18760 {
18761 /* fallthrough */ ;
18762 }
18763 else
18764 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18765
18766 /* Check that the index exists */
18767 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18768 if (!OidIsValid(indexOid))
18769 ereport(ERROR,
18771 errmsg("index \"%s\" for table \"%s\" does not exist",
18772 stmt->name, RelationGetRelationName(rel))));
18773
18774 indexRel = index_open(indexOid, ShareLock);
18775
18776 /* Check that the index is on the relation we're altering. */
18777 if (indexRel->rd_index == NULL ||
18778 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18779 ereport(ERROR,
18781 errmsg("\"%s\" is not an index for table \"%s\"",
18782 RelationGetRelationName(indexRel),
18784
18785 /*
18786 * The AM must support uniqueness, and the index must in fact be unique.
18787 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18788 * exclusion), we can use that too.
18789 */
18790 if ((!indexRel->rd_indam->amcanunique ||
18791 !indexRel->rd_index->indisunique) &&
18792 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18793 ereport(ERROR,
18795 errmsg("cannot use non-unique index \"%s\" as replica identity",
18796 RelationGetRelationName(indexRel))));
18797 /* Deferred indexes are not guaranteed to be always unique. */
18798 if (!indexRel->rd_index->indimmediate)
18799 ereport(ERROR,
18801 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18802 RelationGetRelationName(indexRel))));
18803 /* Expression indexes aren't supported. */
18804 if (RelationGetIndexExpressions(indexRel) != NIL)
18805 ereport(ERROR,
18807 errmsg("cannot use expression index \"%s\" as replica identity",
18808 RelationGetRelationName(indexRel))));
18809 /* Predicate indexes aren't supported. */
18810 if (RelationGetIndexPredicate(indexRel) != NIL)
18811 ereport(ERROR,
18813 errmsg("cannot use partial index \"%s\" as replica identity",
18814 RelationGetRelationName(indexRel))));
18815
18816 /* Check index for nullable columns. */
18817 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18818 {
18819 int16 attno = indexRel->rd_index->indkey.values[key];
18820 Form_pg_attribute attr;
18821
18822 /*
18823 * Reject any other system columns. (Going forward, we'll disallow
18824 * indexes containing such columns in the first place, but they might
18825 * exist in older branches.)
18826 */
18827 if (attno <= 0)
18828 ereport(ERROR,
18830 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18831 RelationGetRelationName(indexRel), attno)));
18832
18833 attr = TupleDescAttr(rel->rd_att, attno - 1);
18834 if (!attr->attnotnull)
18835 ereport(ERROR,
18837 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18838 RelationGetRelationName(indexRel),
18839 NameStr(attr->attname))));
18840 }
18841
18842 /* This index is suitable for use as a replica identity. Mark it. */
18843 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18844
18845 index_close(indexRel, NoLock);
18846}
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition rel.h:535
List * RelationGetIndexPredicate(Relation relation)
Definition relcache.c:5211
List * RelationGetIndexExpressions(Relation relation)
Definition relcache.c:5098
bool amcanunique
Definition amapi.h:259
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 16782 of file tablecmds.c.

16783{
16786 HeapTuple tuple;
16787 Form_pg_class rd_rel;
16788 Oid reloid = RelationGetRelid(rel);
16789
16790 /*
16791 * Shouldn't be called on relations having storage; these are processed in
16792 * phase 3.
16793 */
16794 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16795
16796 /* Get a modifiable copy of the relation's pg_class row. */
16798
16800 if (!HeapTupleIsValid(tuple))
16801 elog(ERROR, "cache lookup failed for relation %u", reloid);
16802 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16803
16804 /* Update the pg_class row. */
16805 oldAccessMethodId = rd_rel->relam;
16806 rd_rel->relam = newAccessMethodId;
16807
16808 /* Leave if no update required */
16809 if (rd_rel->relam == oldAccessMethodId)
16810 {
16811 heap_freetuple(tuple);
16813 return;
16814 }
16815
16816 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16817
16818 /*
16819 * Update the dependency on the new access method. No dependency is added
16820 * if the new access method is InvalidOid (default case). Be very careful
16821 * that this has to compare the previous value stored in pg_class with the
16822 * new one.
16823 */
16824 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16825 {
16827 referenced;
16828
16829 /*
16830 * New access method is defined and there was no dependency
16831 * previously, so record a new one.
16832 */
16836 }
16837 else if (OidIsValid(oldAccessMethodId) &&
16838 !OidIsValid(rd_rel->relam))
16839 {
16840 /*
16841 * There was an access method defined, and no new one, so just remove
16842 * the existing dependency.
16843 */
16847 }
16848 else
16849 {
16851 OidIsValid(rd_rel->relam));
16852
16853 /* Both are valid, so update the dependency */
16856 oldAccessMethodId, rd_rel->relam);
16857 }
16858
16859 /* make the relam and dependency changes visible */
16861
16863
16864 heap_freetuple(tuple);
16866}

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

18996{
18998 HeapTuple tuple;
19001 char *compression;
19002 char cmethod;
19003 ObjectAddress address;
19004
19005 compression = strVal(newValue);
19006
19008
19009 /* copy the cache entry so we can scribble on it below */
19011 if (!HeapTupleIsValid(tuple))
19012 ereport(ERROR,
19014 errmsg("column \"%s\" of relation \"%s\" does not exist",
19016
19017 /* prevent them from altering a system attribute */
19019 attnum = atttableform->attnum;
19020 if (attnum <= 0)
19021 ereport(ERROR,
19023 errmsg("cannot alter system column \"%s\"", column)));
19024
19025 /*
19026 * Check that column type is compressible, then get the attribute
19027 * compression method code
19028 */
19029 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
19030
19031 /* update pg_attribute entry */
19032 atttableform->attcompression = cmethod;
19033 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
19034
19036 RelationGetRelid(rel),
19037 attnum);
19038
19039 /*
19040 * Apply the change to indexes as well (only for simple index columns,
19041 * matching behavior of index.c ConstructTupleDescriptor()).
19042 */
19044 false, 0,
19045 true, cmethod,
19046 lockmode);
19047
19048 heap_freetuple(tuple);
19049
19051
19052 /* make changes visible */
19054
19056 RelationGetRelid(rel), attnum);
19057 return address;
19058}
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:9207

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

8694{
8695 HeapTuple tuple;
8698 char attgenerated;
8699 bool rewrite;
8701 ObjectAddress address;
8702 Expr *defval;
8705
8707 if (!HeapTupleIsValid(tuple))
8708 ereport(ERROR,
8710 errmsg("column \"%s\" of relation \"%s\" does not exist",
8712
8714
8715 attnum = attTup->attnum;
8716 if (attnum <= 0)
8717 ereport(ERROR,
8719 errmsg("cannot alter system column \"%s\"",
8720 colName)));
8721
8722 attgenerated = attTup->attgenerated;
8723 if (!attgenerated)
8724 ereport(ERROR,
8726 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8728
8729 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8730 tab->verify_new_notnull = true;
8731
8732 /*
8733 * We need to prevent this because a change of expression could affect a
8734 * row filter and inject expressions that are not permitted in a row
8735 * filter. XXX We could try to have a more precise check to catch only
8736 * publications with row filters, or even re-verify the row filter
8737 * expressions.
8738 */
8739 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8741 ereport(ERROR,
8743 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8744 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8746
8747 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8748
8749 ReleaseSysCache(tuple);
8750
8751 if (rewrite)
8752 {
8753 /*
8754 * Clear all the missing values if we're rewriting the table, since
8755 * this renders them pointless.
8756 */
8758
8759 /* make sure we don't conflict with later attribute modifications */
8761 }
8762
8763 /*
8764 * Find everything that depends on the column (constraints, indexes, etc),
8765 * and record enough information to let us recreate the objects.
8766 */
8768
8769 /*
8770 * Drop the dependency records of the GENERATED expression, in particular
8771 * its INTERNAL dependency on the column, which would otherwise cause
8772 * dependency.c to refuse to perform the deletion.
8773 */
8775 if (!OidIsValid(attrdefoid))
8776 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8777 RelationGetRelid(rel), attnum);
8779
8780 /* Make above changes visible */
8782
8783 /*
8784 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8785 * safety, but at present we do not expect anything to depend on the
8786 * expression.
8787 */
8789 false, false);
8790
8791 /* Prepare to store the new expression, in the catalogs */
8793 rawEnt->attnum = attnum;
8794 rawEnt->raw_default = newExpr;
8795 rawEnt->generated = attgenerated;
8796
8797 /* Store the generated expression */
8799 false, true, false, NULL);
8800
8801 /* Make above new expression visible */
8803
8804 if (rewrite)
8805 {
8806 /* Prepare for table rewrite */
8807 defval = (Expr *) build_column_default(rel, attnum);
8808
8810 newval->attnum = attnum;
8811 newval->expr = expression_planner(defval);
8812 newval->is_generated = true;
8813
8814 tab->newvals = lappend(tab->newvals, newval);
8816 }
8817
8818 /* Drop any pg_statistic entry for the column */
8820
8822 RelationGetRelid(rel), attnum);
8823
8825 RelationGetRelid(rel), attnum);
8826 return address;
8827}
List * GetRelationIncludedPublications(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(), GetRelationIncludedPublications(), 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 8461 of file tablecmds.c.

8463{
8466 HeapTuple tuple;
8470 ObjectAddress address;
8471 bool ispartitioned;
8472
8473 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8474 if (ispartitioned && !recurse)
8475 ereport(ERROR,
8477 errmsg("cannot change identity column of only the partitioned table"),
8478 errhint("Do not specify the ONLY keyword.")));
8479
8480 if (rel->rd_rel->relispartition && !recursing)
8481 ereport(ERROR,
8483 errmsg("cannot change identity column of a partition"));
8484
8485 foreach(option, castNode(List, def))
8486 {
8488
8489 if (strcmp(defel->defname, "generated") == 0)
8490 {
8491 if (generatedEl)
8492 ereport(ERROR,
8494 errmsg("conflicting or redundant options")));
8496 }
8497 else
8498 elog(ERROR, "option \"%s\" not recognized",
8499 defel->defname);
8500 }
8501
8502 /*
8503 * Even if there is nothing to change here, we run all the checks. There
8504 * will be a subsequent ALTER SEQUENCE that relies on everything being
8505 * there.
8506 */
8507
8510 if (!HeapTupleIsValid(tuple))
8511 ereport(ERROR,
8513 errmsg("column \"%s\" of relation \"%s\" does not exist",
8515
8517 attnum = attTup->attnum;
8518
8519 if (attnum <= 0)
8520 ereport(ERROR,
8522 errmsg("cannot alter system column \"%s\"",
8523 colName)));
8524
8525 if (!attTup->attidentity)
8526 ereport(ERROR,
8528 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8530
8531 if (generatedEl)
8532 {
8533 attTup->attidentity = defGetInt32(generatedEl);
8534 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8535
8537 RelationGetRelid(rel),
8538 attTup->attnum);
8540 RelationGetRelid(rel), attnum);
8541 }
8542 else
8543 address = InvalidObjectAddress;
8544
8545 heap_freetuple(tuple);
8547
8548 /*
8549 * Recurse to propagate the identity change to partitions. Identity is not
8550 * inherited in regular inheritance children.
8551 */
8552 if (generatedEl && recurse && ispartitioned)
8553 {
8554 List *children;
8555 ListCell *lc;
8556
8557 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8558
8559 foreach(lc, children)
8560 {
8562
8564 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8566 }
8567 }
8568
8569 return address;
8570}
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 8003 of file tablecmds.c.

8005{
8006 HeapTuple tuple;
8008 ObjectAddress address;
8009 Constraint *constraint;
8011 List *cooked;
8012 bool is_no_inherit = false;
8013
8014 /* Guard against stack overflow due to overly deep inheritance tree. */
8016
8017 /* At top level, permission check was done in ATPrepCmd, else do it */
8018 if (recursing)
8019 {
8022 Assert(conName != NULL);
8023 }
8024
8027 ereport(ERROR,
8029 errmsg("column \"%s\" of relation \"%s\" does not exist",
8031
8032 /* Prevent them from altering a system attribute */
8033 if (attnum <= 0)
8034 ereport(ERROR,
8036 errmsg("cannot alter system column \"%s\"",
8037 colName)));
8038
8039 /* See if there's already a constraint */
8041 if (HeapTupleIsValid(tuple))
8042 {
8044 bool changed = false;
8045
8046 /*
8047 * Don't let a NO INHERIT constraint be changed into inherit.
8048 */
8049 if (conForm->connoinherit && recurse)
8050 ereport(ERROR,
8052 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
8053 NameStr(conForm->conname),
8055
8056 /*
8057 * If we find an appropriate constraint, we're almost done, but just
8058 * need to change some properties on it: if we're recursing, increment
8059 * coninhcount; if not, set conislocal if not already set.
8060 */
8061 if (recursing)
8062 {
8063 if (pg_add_s16_overflow(conForm->coninhcount, 1,
8064 &conForm->coninhcount))
8065 ereport(ERROR,
8067 errmsg("too many inheritance parents"));
8068 changed = true;
8069 }
8070 else if (!conForm->conislocal)
8071 {
8072 conForm->conislocal = true;
8073 changed = true;
8074 }
8075 else if (!conForm->convalidated)
8076 {
8077 /*
8078 * Flip attnotnull and convalidated, and also validate the
8079 * constraint.
8080 */
8081 return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
8082 recurse, recursing, lockmode);
8083 }
8084
8085 if (changed)
8086 {
8088
8090
8091 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
8094 }
8095
8096 if (changed)
8097 return address;
8098 else
8099 return InvalidObjectAddress;
8100 }
8101
8102 /*
8103 * If we're asked not to recurse, and children exist, raise an error for
8104 * partitioned tables. For inheritance, we act as if NO INHERIT had been
8105 * specified.
8106 */
8107 if (!recurse &&
8109 NoLock) != NIL)
8110 {
8111 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8112 ereport(ERROR,
8114 errmsg("constraint must be added to child tables too"),
8115 errhint("Do not specify the ONLY keyword."));
8116 else
8117 is_no_inherit = true;
8118 }
8119
8120 /*
8121 * No constraint exists; we must add one. First determine a name to use,
8122 * if we haven't already.
8123 */
8124 if (!recursing)
8125 {
8126 Assert(conName == NULL);
8128 colName, "not_null",
8130 NIL);
8131 }
8132
8134 constraint->is_no_inherit = is_no_inherit;
8135 constraint->conname = conName;
8136
8137 /* and do it */
8138 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8139 false, !recursing, false, NULL);
8140 ccon = linitial(cooked);
8141 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8142
8143 /* Mark pg_attribute.attnotnull for the column and queue validation */
8144 set_attnotnull(wqueue, rel, attnum, true, true);
8145
8147 RelationGetRelid(rel), attnum);
8148
8149 /*
8150 * Recurse to propagate the constraint to children that don't have one.
8151 */
8152 if (recurse)
8153 {
8154 List *children;
8155
8157 lockmode);
8158
8159 foreach_oid(childoid, children)
8160 {
8162
8164
8166 recurse, true, lockmode);
8168 }
8169 }
8170
8171 return address;
8172}
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 9128 of file tablecmds.c.

9130{
9132 HeapTuple tuple,
9133 newtuple;
9136 Datum datum,
9137 newOptions;
9138 bool isnull;
9139 ObjectAddress address;
9143
9145
9147
9148 if (!HeapTupleIsValid(tuple))
9149 ereport(ERROR,
9151 errmsg("column \"%s\" of relation \"%s\" does not exist",
9154
9155 attnum = attrtuple->attnum;
9156 if (attnum <= 0)
9157 ereport(ERROR,
9159 errmsg("cannot alter system column \"%s\"",
9160 colName)));
9161
9162 /* Generate new proposed attoptions (text array) */
9164 &isnull);
9165 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9167 false, isReset);
9168 /* Validate new options */
9170
9171 /* Build new tuple. */
9172 memset(repl_null, false, sizeof(repl_null));
9173 memset(repl_repl, false, sizeof(repl_repl));
9174 if (newOptions != (Datum) 0)
9176 else
9181
9182 /* Update system catalog. */
9183 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9184
9186 RelationGetRelid(rel),
9187 attrtuple->attnum);
9189 RelationGetRelid(rel), attnum);
9190
9191 heap_freetuple(newtuple);
9192
9193 ReleaseSysCache(tuple);
9194
9196
9197 return address;
9198}
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 16902 of file tablecmds.c.

16904{
16905 Oid relid;
16907 HeapTuple tuple;
16908 HeapTuple newtuple;
16909 Datum datum;
16914 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16915
16917 return; /* nothing to do */
16918
16920
16921 /* Fetch heap tuple */
16922 relid = RelationGetRelid(rel);
16924 if (!HeapTupleIsValid(tuple))
16925 elog(ERROR, "cache lookup failed for relation %u", relid);
16926
16928 {
16929 /*
16930 * If we're supposed to replace the reloptions list, we just pretend
16931 * there were none before.
16932 */
16933 datum = (Datum) 0;
16934 }
16935 else
16936 {
16937 bool isnull;
16938
16939 /* Get the old reloptions */
16941 &isnull);
16942 if (isnull)
16943 datum = (Datum) 0;
16944 }
16945
16946 /* Generate new proposed reloptions (text array) */
16949
16950 /* Validate */
16951 switch (rel->rd_rel->relkind)
16952 {
16953 case RELKIND_RELATION:
16954 case RELKIND_MATVIEW:
16955 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16956 break;
16959 break;
16960 case RELKIND_VIEW:
16962 break;
16963 case RELKIND_INDEX:
16966 break;
16967 case RELKIND_TOASTVALUE:
16968 /* fall through to error -- shouldn't ever get here */
16969 default:
16970 ereport(ERROR,
16972 errmsg("cannot set options for relation \"%s\"",
16974 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16975 break;
16976 }
16977
16978 /* Special-case validation of view options */
16979 if (rel->rd_rel->relkind == RELKIND_VIEW)
16980 {
16983 ListCell *cell;
16984 bool check_option = false;
16985
16986 foreach(cell, view_options)
16987 {
16988 DefElem *defel = (DefElem *) lfirst(cell);
16989
16990 if (strcmp(defel->defname, "check_option") == 0)
16991 check_option = true;
16992 }
16993
16994 /*
16995 * If the check option is specified, look to see if the view is
16996 * actually auto-updatable or not.
16997 */
16998 if (check_option)
16999 {
17000 const char *view_updatable_error =
17002
17004 ereport(ERROR,
17006 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
17007 errhint("%s", _(view_updatable_error))));
17008 }
17009 }
17010
17011 /*
17012 * All we need do here is update the pg_class row; the new options will be
17013 * propagated into relcaches during post-commit cache inval.
17014 */
17015 memset(repl_val, 0, sizeof(repl_val));
17016 memset(repl_null, false, sizeof(repl_null));
17017 memset(repl_repl, false, sizeof(repl_repl));
17018
17019 if (newOptions != (Datum) 0)
17021 else
17023
17025
17026 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17028
17029 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17031
17033
17034 heap_freetuple(newtuple);
17035
17036 ReleaseSysCache(tuple);
17037
17038 /* repeat the whole exercise for the toast table, if there's one */
17039 if (OidIsValid(rel->rd_rel->reltoastrelid))
17040 {
17042 Oid toastid = rel->rd_rel->reltoastrelid;
17043
17044 toastrel = table_open(toastid, lockmode);
17045
17046 /* Fetch heap tuple */
17048 if (!HeapTupleIsValid(tuple))
17049 elog(ERROR, "cache lookup failed for relation %u", toastid);
17050
17052 {
17053 /*
17054 * If we're supposed to replace the reloptions list, we just
17055 * pretend there were none before.
17056 */
17057 datum = (Datum) 0;
17058 }
17059 else
17060 {
17061 bool isnull;
17062
17063 /* Get the old reloptions */
17065 &isnull);
17066 if (isnull)
17067 datum = (Datum) 0;
17068 }
17069
17071 false, operation == AT_ResetRelOptions);
17072
17074
17075 memset(repl_val, 0, sizeof(repl_val));
17076 memset(repl_null, false, sizeof(repl_null));
17077 memset(repl_repl, false, sizeof(repl_repl));
17078
17079 if (newOptions != (Datum) 0)
17081 else
17083
17085
17086 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17088
17089 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17090
17093 InvalidOid, true);
17094
17095 heap_freetuple(newtuple);
17096
17097 ReleaseSysCache(tuple);
17098
17100 }
17101
17103}
static DataChecksumsWorkerOperation operation
#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:61
Query * get_view_query(Relation view)
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
amoptions_function amoptions
Definition amapi.h:305
HeapTuple SearchSysCacheLocked1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:283

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, operation, 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 18852 of file tablecmds.c.

18853{
18855 Oid relid;
18856 HeapTuple tuple;
18857
18858 relid = RelationGetRelid(rel);
18859
18860 /* Pull the record for this relation and update it */
18862
18864
18865 if (!HeapTupleIsValid(tuple))
18866 elog(ERROR, "cache lookup failed for relation %u", relid);
18867
18869 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18870
18872 RelationGetRelid(rel), 0);
18873
18875 heap_freetuple(tuple);
18876}

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

8984{
8985 int newtarget = 0;
8986 bool newtarget_default;
8988 HeapTuple tuple,
8989 newtuple;
8992 ObjectAddress address;
8996
8997 /*
8998 * We allow referencing columns by numbers only for indexes, since table
8999 * column numbers could contain gaps if columns are later dropped.
9000 */
9001 if (rel->rd_rel->relkind != RELKIND_INDEX &&
9002 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
9003 !colName)
9004 ereport(ERROR,
9006 errmsg("cannot refer to non-index column by number")));
9007
9008 /* -1 was used in previous versions for the default setting */
9009 if (newValue && intVal(newValue) != -1)
9010 {
9012 newtarget_default = false;
9013 }
9014 else
9015 newtarget_default = true;
9016
9017 if (!newtarget_default)
9018 {
9019 /*
9020 * Limit target to a sane range
9021 */
9022 if (newtarget < 0)
9023 {
9024 ereport(ERROR,
9026 errmsg("statistics target %d is too low",
9027 newtarget)));
9028 }
9030 {
9034 errmsg("lowering statistics target to %d",
9035 newtarget)));
9036 }
9037 }
9038
9040
9041 if (colName)
9042 {
9044
9045 if (!HeapTupleIsValid(tuple))
9046 ereport(ERROR,
9048 errmsg("column \"%s\" of relation \"%s\" does not exist",
9050 }
9051 else
9052 {
9054
9055 if (!HeapTupleIsValid(tuple))
9056 ereport(ERROR,
9058 errmsg("column number %d of relation \"%s\" does not exist",
9060 }
9061
9063
9064 attnum = attrtuple->attnum;
9065 if (attnum <= 0)
9066 ereport(ERROR,
9068 errmsg("cannot alter system column \"%s\"",
9069 colName)));
9070
9071 /*
9072 * Prevent this as long as the ANALYZE code skips virtual generated
9073 * columns.
9074 */
9075 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
9076 ereport(ERROR,
9078 errmsg("cannot alter statistics on virtual generated column \"%s\"",
9079 colName)));
9080
9081 if (rel->rd_rel->relkind == RELKIND_INDEX ||
9082 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
9083 {
9084 if (attnum > rel->rd_index->indnkeyatts)
9085 ereport(ERROR,
9087 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9088 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9089 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9090 ereport(ERROR,
9092 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9093 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9094 errhint("Alter statistics on table column instead.")));
9095 }
9096
9097 /* Build new tuple. */
9098 memset(repl_null, false, sizeof(repl_null));
9099 memset(repl_repl, false, sizeof(repl_repl));
9100 if (!newtarget_default)
9102 else
9107 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9108
9110 RelationGetRelid(rel),
9111 attrtuple->attnum);
9113 RelationGetRelid(rel), attnum);
9114
9115 heap_freetuple(newtuple);
9116
9117 ReleaseSysCache(tuple);
9118
9120
9121 return address;
9122}
static Datum Int16GetDatum(int16 X)
Definition postgres.h:172
HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum)
Definition syscache.c:539
#define MAX_STATISTICS_TARGET
Definition vacuum.h:350
#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 9270 of file tablecmds.c.

9271{
9273 HeapTuple tuple;
9276 ObjectAddress address;
9277
9279
9281
9282 if (!HeapTupleIsValid(tuple))
9283 ereport(ERROR,
9285 errmsg("column \"%s\" of relation \"%s\" does not exist",
9288
9289 attnum = attrtuple->attnum;
9290 if (attnum <= 0)
9291 ereport(ERROR,
9293 errmsg("cannot alter system column \"%s\"",
9294 colName)));
9295
9296 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9297
9298 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9299
9301 RelationGetRelid(rel),
9302 attrtuple->attnum);
9303
9304 /*
9305 * Apply the change to indexes as well (only for simple index columns,
9306 * matching behavior of index.c ConstructTupleDescriptor()).
9307 */
9309 true, attrtuple->attstorage,
9310 false, 0,
9311 lockmode);
9312
9313 heap_freetuple(tuple);
9314
9316
9318 RelationGetRelid(rel), attnum);
9319 return address;
9320}
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 17110 of file tablecmds.c.

17111{
17112 Relation rel;
17117 ListCell *lc;
17118
17119 /*
17120 * Need lock here in case we are recursing to toast table or index
17121 */
17122 rel = relation_open(tableOid, lockmode);
17123
17124 /* Check first if relation can be moved to new tablespace */
17125 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
17126 {
17128 RelationGetRelid(rel), 0);
17129 relation_close(rel, NoLock);
17130 return;
17131 }
17132
17133 reltoastrelid = rel->rd_rel->reltoastrelid;
17134 /* Fetch the list of indexes on toast relation if necessary */
17136 {
17138
17140 relation_close(toastRel, lockmode);
17141 }
17142
17143 /*
17144 * Relfilenumbers are not unique in databases across tablespaces, so we
17145 * need to allocate a new one in the new tablespace.
17146 */
17147 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
17148 rel->rd_rel->relpersistence);
17149
17150 /* Open old and new relation */
17151 newrlocator = rel->rd_locator;
17153 newrlocator.spcOid = newTableSpace;
17154
17155 /* hand off to AM to actually create new rel storage and copy the data */
17156 if (rel->rd_rel->relkind == RELKIND_INDEX)
17157 {
17159 }
17160 else
17161 {
17162 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
17164 }
17165
17166 /*
17167 * Update the pg_class row.
17168 *
17169 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
17170 * executed on pg_class or its indexes (the above copy wouldn't contain
17171 * the updated pg_class entry), but that's forbidden with
17172 * CheckRelationTableSpaceMove().
17173 */
17174 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
17175
17177
17179
17180 relation_close(rel, NoLock);
17181
17182 /* Make sure the reltablespace change is visible */
17184
17185 /* Move associated toast relation and/or indexes, too */
17187 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
17188 foreach(lc, reltoastidxids)
17189 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
17190
17191 /* Clean up */
17193}
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition catalog.c:557
void RelationAssumeNewRelfilelocator(Relation relation)
Definition relcache.c:3978
Oid RelFileNumber
Definition relpath.h:25
RelFileNumber relNumber
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition tableam.h:1717
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition tablecmds.c:3750
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition tablecmds.c:3807
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 17203 of file tablecmds.c.

17204{
17205 /*
17206 * Shouldn't be called on relations having storage; these are processed in
17207 * phase 3.
17208 */
17209 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
17210
17211 /* check if relation can be moved to its new tablespace */
17212 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
17213 {
17215 RelationGetRelid(rel),
17216 0);
17217 return;
17218 }
17219
17220 /* Update can be done, so change reltablespace */
17221 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
17222
17224
17225 /* Make sure the reltablespace change is visible */
17227}

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

23720{
23724 *listptr2;
23725 bool isSameName = false;
23726 char tmpRelName[NAMEDATALEN];
23727 List *newPartRels = NIL;
23728 List *extDepState = NIL;
23729 ObjectAddress object;
23731 Oid save_userid;
23732 int save_sec_context;
23733 int save_nestlevel;
23735
23737
23738 /*
23739 * Partition is already locked in the transformPartitionCmdForSplit
23740 * function.
23741 */
23743
23745
23746 /* Check descriptions of new partitions. */
23748 {
23750
23751 /* Look up the existing relation by the new partition name. */
23753
23754 /*
23755 * This would fail later on anyway if the relation already exists. But
23756 * by catching it here, we can emit a nicer error message.
23757 */
23759 /* One new partition can have the same name as a split partition. */
23760 isSameName = true;
23761 else if (OidIsValid(existingRelid))
23762 ereport(ERROR,
23764 errmsg("relation \"%s\" already exists", sps->name->relname));
23765 }
23766
23767 /*
23768 * Collect extension dependencies from indexes on the split partition. We
23769 * must do this before detaching it, so we can restore the dependencies on
23770 * the new partitions' indexes later.
23771 */
23773
23776
23777 /* Detach the split partition. */
23779
23780 /*
23781 * Perform a preliminary check to determine whether it's safe to drop the
23782 * split partition before we actually do so later. After merging rows into
23783 * the new partitions via SplitPartitionMoveRows, all old partitions need
23784 * to be dropped. However, since the drop behavior is DROP_RESTRICT and
23785 * the merge process (SplitPartitionMoveRows) can be time-consuming,
23786 * performing an early check on the drop eligibility of old partitions is
23787 * preferable.
23788 */
23789 object.objectId = splitRelOid;
23790 object.classId = RelationRelationId;
23791 object.objectSubId = 0;
23793
23794 /*
23795 * If a new partition has the same name as the split partition, then we
23796 * should rename the split partition to reuse its name.
23797 */
23798 if (isSameName)
23799 {
23800 /*
23801 * We must bump the command counter to make the split partition tuple
23802 * visible for renaming.
23803 */
23805 /* Rename partition. */
23806 sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23808
23809 /*
23810 * We must bump the command counter to make the split partition tuple
23811 * visible after renaming.
23812 */
23814 }
23815
23816 /* Create new partitions (like a split partition), without indexes. */
23818 {
23820
23822 splitRel->rd_rel->relowner);
23824 }
23825
23826 /*
23827 * Switch to the table owner's userid, so that any index functions are run
23828 * as that user. Also, lockdown security-restricted operations and
23829 * arrange to make GUC variable changes local to this command.
23830 *
23831 * Need to do it after determining the namespace in the
23832 * createPartitionTable() call.
23833 */
23834 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23835 SetUserIdAndSecContext(splitRel->rd_rel->relowner,
23836 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23837 save_nestlevel = NewGUCNestLevel();
23839
23840 /* Copy data from the split partition to the new partitions. */
23842 /* Keep the lock until commit. */
23844
23845 /* Attach new partitions to the partitioned table. */
23847 {
23850
23851 /*
23852 * wqueue = NULL: verification for each cloned constraint is not
23853 * needed.
23854 */
23855 attachPartitionTable(NULL, rel, newPartRel, sps->bound);
23856
23857 /*
23858 * Apply extension dependencies to the new partition's indexes. This
23859 * preserves any "DEPENDS ON EXTENSION" settings from the split
23860 * partition.
23861 */
23863
23864 /* Keep the lock until commit. */
23866 }
23867
23869
23870 /* Drop the split partition. */
23871 object.classId = RelationRelationId;
23872 object.objectId = splitRelOid;
23873 object.objectSubId = 0;
23874 /* Probably DROP_CASCADE is not needed. */
23875 performDeletion(&object, DROP_RESTRICT, 0);
23876
23877 /* Roll back any GUC changes executed by index functions. */
23878 AtEOXact_GUC(false, save_nestlevel);
23879
23880 /* Restore the userid and security context. */
23881 SetUserIdAndSecContext(save_userid, save_sec_context);
23882}
struct RelationData * Relation
Definition genam.h:30
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:550
static void SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel, List *partlist, List *newPartRels)

References applyPartitionIndexExtDeps(), AtEOXact_GUC(), attachPartitionTable(), collectPartitionIndexExtDeps(), CommandCounterIncrement(), createPartitionTable(), detachPartitionTable(), DROP_RESTRICT, ereport, errcode(), errmsg, ERROR, fb(), forboth, foreach_node, freePartitionIndexExtDeps(), get_default_oid_from_partdesc(), GetUserIdAndSecContext(), lappend(), lfirst, list_free(), list_make1_oid, 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 13161 of file tablecmds.c.

13163{
13165 SysScanDesc scan;
13166 ScanKeyData skey[3];
13167 HeapTuple tuple;
13169 ObjectAddress address;
13170
13172
13173 /*
13174 * Find and check the target constraint
13175 */
13176 ScanKeyInit(&skey[0],
13180 ScanKeyInit(&skey[1],
13184 ScanKeyInit(&skey[2],
13189 true, NULL, 3, skey);
13190
13191 /* There can be at most one matching row */
13192 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
13193 ereport(ERROR,
13195 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13197
13198 con = (Form_pg_constraint) GETSTRUCT(tuple);
13199 if (con->contype != CONSTRAINT_FOREIGN &&
13200 con->contype != CONSTRAINT_CHECK &&
13201 con->contype != CONSTRAINT_NOTNULL)
13202 ereport(ERROR,
13204 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
13206 errdetail("This operation is not supported for this type of constraint."));
13207
13208 if (!con->conenforced)
13209 ereport(ERROR,
13211 errmsg("cannot validate NOT ENFORCED constraint")));
13212
13213 if (!con->convalidated)
13214 {
13215 if (con->contype == CONSTRAINT_FOREIGN)
13216 {
13217 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
13218 tuple, lockmode);
13219 }
13220 else if (con->contype == CONSTRAINT_CHECK)
13221 {
13223 tuple, recurse, recursing, lockmode);
13224 }
13225 else if (con->contype == CONSTRAINT_NOTNULL)
13226 {
13228 tuple, recurse, recursing, lockmode);
13229 }
13230
13231 ObjectAddressSet(address, ConstraintRelationId, con->oid);
13232 }
13233 else
13234 address = InvalidObjectAddress; /* already validated */
13235
13236 systable_endscan(scan);
13237
13239
13240 return address;
13241}
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 6648 of file tablecmds.c.

6649{
6650 Oid relid = RelationGetRelid(rel);
6651 AlteredTableInfo *tab;
6652 ListCell *ltab;
6653
6654 foreach(ltab, *wqueue)
6655 {
6656 tab = (AlteredTableInfo *) lfirst(ltab);
6657 if (tab->relid == relid)
6658 return tab;
6659 }
6660
6661 /*
6662 * Not there, so add it. Note that we make a copy of the relation's
6663 * existing descriptor before anything interesting can happen to it.
6664 */
6666 tab->relid = relid;
6667 tab->rel = NULL; /* set later */
6668 tab->relkind = rel->rd_rel->relkind;
6671 tab->chgAccessMethod = false;
6674 tab->chgPersistence = false;
6675
6676 *wqueue = lappend(*wqueue, tab);
6677
6678 return tab;
6679}
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition tupdesc.c:336

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(), ATExecAlterCheckConstrEnforceability(), ATExecAlterFKConstrEnforceability(), 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 5795 of file tablecmds.c.

5798{
5802 List *afterStmts;
5803 ListCell *lc;
5804
5805 /* Gin up an AlterTableStmt with just this subcommand and this table */
5806 atstmt->relation =
5809 -1);
5810 atstmt->relation->inh = recurse;
5811 atstmt->cmds = list_make1(cmd);
5812 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5813 atstmt->missing_ok = false;
5814
5815 /* Transform the AlterTableStmt */
5817 atstmt,
5818 context->queryString,
5819 &beforeStmts,
5820 &afterStmts);
5821
5822 /* Execute any statements that should happen before these subcommand(s) */
5823 foreach(lc, beforeStmts)
5824 {
5825 Node *stmt = (Node *) lfirst(lc);
5826
5829 }
5830
5831 /* Examine the transformed subcommands and schedule them appropriately */
5832 foreach(lc, atstmt->cmds)
5833 {
5836
5837 /*
5838 * This switch need only cover the subcommand types that can be added
5839 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5840 * executing the subcommand immediately, as a substitute for the
5841 * original subcommand. (Note, however, that this does cause
5842 * AT_AddConstraint subcommands to be rescheduled into later passes,
5843 * which is important for index and foreign key constraints.)
5844 *
5845 * We assume we needn't do any phase-1 checks for added subcommands.
5846 */
5847 switch (cmd2->subtype)
5848 {
5849 case AT_AddIndex:
5851 break;
5854 break;
5855 case AT_AddConstraint:
5856 /* Recursion occurs during execution phase */
5857 if (recurse)
5858 cmd2->recurse = true;
5859 switch (castNode(Constraint, cmd2->def)->contype)
5860 {
5861 case CONSTR_NOTNULL:
5863 break;
5864 case CONSTR_PRIMARY:
5865 case CONSTR_UNIQUE:
5866 case CONSTR_EXCLUSION:
5868 break;
5869 default:
5871 break;
5872 }
5873 break;
5875 /* This command never recurses */
5876 /* No command-specific prep needed */
5878 break;
5879 default:
5880 pass = cur_pass;
5881 break;
5882 }
5883
5884 if (pass < cur_pass)
5885 {
5886 /* Cannot schedule into a pass we already finished */
5887 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5888 pass);
5889 }
5890 else if (pass > cur_pass)
5891 {
5892 /* OK, queue it up for later */
5893 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5894 }
5895 else
5896 {
5897 /*
5898 * We should see at most one subcommand for the current pass,
5899 * which is the transformed version of the original subcommand.
5900 */
5901 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5902 {
5903 /* Found the transformed version of our subcommand */
5904 newcmd = cmd2;
5905 }
5906 else
5907 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5908 pass);
5909 }
5910 }
5911
5912 /* Queue up any after-statements to happen at the end */
5913 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5914
5915 return newcmd;
5916}
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:189
void ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context)
Definition utility.c:1971

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

15693{
15694 ObjectAddress obj;
15695 ObjectAddresses *objects;
15698
15699 /*
15700 * Collect all the constraints and indexes to drop so we can process them
15701 * in a single call. That way we don't have to worry about dependencies
15702 * among them.
15703 */
15704 objects = new_object_addresses();
15705
15706 /*
15707 * Re-parse the index and constraint definitions, and attach them to the
15708 * appropriate work queue entries. We do this before dropping because in
15709 * the case of a constraint on another table, we might not yet have
15710 * exclusive lock on the table the constraint is attached to, and we need
15711 * to get that before reparsing/dropping. (That's possible at least for
15712 * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15713 * requires a dependency on the target table's composite type in the other
15714 * table's constraint expressions.)
15715 *
15716 * We can't rely on the output of deparsing to tell us which relation to
15717 * operate on, because concurrent activity might have made the name
15718 * resolve differently. Instead, we've got to use the OID of the
15719 * constraint or index we're processing to figure out which relation to
15720 * operate on.
15721 */
15724 {
15726 HeapTuple tup;
15728 Oid relid;
15729 Oid confrelid;
15730 bool conislocal;
15731
15733 if (!HeapTupleIsValid(tup)) /* should not happen */
15734 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15736 if (OidIsValid(con->conrelid))
15737 relid = con->conrelid;
15738 else
15739 {
15740 /* must be a domain constraint */
15741 relid = get_typ_typrelid(getBaseType(con->contypid));
15742 if (!OidIsValid(relid))
15743 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15744 }
15745 confrelid = con->confrelid;
15746 conislocal = con->conislocal;
15748
15750 add_exact_object_address(&obj, objects);
15751
15752 /*
15753 * If the constraint is inherited (only), we don't want to inject a
15754 * new definition here; it'll get recreated when
15755 * ATAddCheckNNConstraint recurses from adding the parent table's
15756 * constraint. But we had to carry the info this far so that we can
15757 * drop the constraint below.
15758 */
15759 if (!conislocal)
15760 continue;
15761
15762 /*
15763 * When rebuilding another table's constraint that references the
15764 * table we're modifying, we might not yet have any lock on the other
15765 * table, so get one now. We'll need AccessExclusiveLock for the DROP
15766 * CONSTRAINT step, so there's no value in asking for anything weaker.
15767 */
15768 if (relid != tab->relid)
15770
15771 ATPostAlterTypeParse(oldId, relid, confrelid,
15772 (char *) lfirst(def_item),
15773 wqueue, lockmode, tab->rewrite);
15774 }
15777 {
15779 Oid relid;
15780
15781 relid = IndexGetRelation(oldId, false);
15782
15783 /*
15784 * As above, make sure we have lock on the index's table if it's not
15785 * the same table.
15786 */
15787 if (relid != tab->relid)
15789
15791 (char *) lfirst(def_item),
15792 wqueue, lockmode, tab->rewrite);
15793
15795 add_exact_object_address(&obj, objects);
15796 }
15797
15798 /* add dependencies for new statistics */
15801 {
15803 Oid relid;
15804
15805 relid = StatisticsGetRelation(oldId, false);
15806
15807 /*
15808 * As above, make sure we have lock on the statistics object's table
15809 * if it's not the same table. However, we take
15810 * ShareUpdateExclusiveLock here, aligning with the lock level used in
15811 * CreateStatistics and RemoveStatisticsById.
15812 *
15813 * CAUTION: this should be done after all cases that grab
15814 * AccessExclusiveLock, else we risk causing deadlock due to needing
15815 * to promote our table lock.
15816 */
15817 if (relid != tab->relid)
15819
15821 (char *) lfirst(def_item),
15822 wqueue, lockmode, tab->rewrite);
15823
15825 add_exact_object_address(&obj, objects);
15826 }
15827
15828 /*
15829 * Queue up command to restore replica identity index marking
15830 */
15831 if (tab->replicaIdentityIndex)
15832 {
15835
15836 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15837 subcmd->name = tab->replicaIdentityIndex;
15839 cmd->def = (Node *) subcmd;
15840
15841 /* do it after indexes and constraints */
15844 }
15845
15846 /*
15847 * Queue up command to restore marking of index used for cluster.
15848 */
15849 if (tab->clusterOnIndex)
15850 {
15852
15853 cmd->subtype = AT_ClusterOn;
15854 cmd->name = tab->clusterOnIndex;
15855
15856 /* do it after indexes and constraints */
15859 }
15860
15861 /*
15862 * It should be okay to use DROP_RESTRICT here, since nothing else should
15863 * be depending on these objects.
15864 */
15866
15867 free_object_addresses(objects);
15868
15869 /*
15870 * The objects will get recreated during subsequent passes over the work
15871 * queue.
15872 */
15873}
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition index.c:3604
Oid get_typ_typrelid(Oid typid)
Definition lsyscache.c:3039
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition statscmds.c:954
List * changedConstraintDefs
Definition tablecmds.c:207
List * changedStatisticsDefs
Definition tablecmds.c:213
char * clusterOnIndex
Definition tablecmds.c:211
char * replicaIdentityIndex
Definition tablecmds.c:210
List * changedStatisticsOids
Definition tablecmds.c:212
List * changedIndexDefs
Definition tablecmds.c:209
List * changedIndexOids
Definition tablecmds.c:208
List * changedConstraintOids
Definition tablecmds.c:206
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 15884 of file tablecmds.c.

15886{
15890 Relation rel;
15891
15892 /*
15893 * We expect that we will get only ALTER TABLE and CREATE INDEX
15894 * statements. Hence, there is no need to pass them through
15895 * parse_analyze_*() or the rewriter, but instead we need to pass them
15896 * through parse_utilcmd.c to make them ready for execution.
15897 */
15901 {
15903 Node *stmt = rs->stmt;
15904
15905 if (IsA(stmt, IndexStmt))
15908 (IndexStmt *) stmt,
15909 cmd));
15910 else if (IsA(stmt, AlterTableStmt))
15911 {
15913 List *afterStmts;
15914
15916 (AlterTableStmt *) stmt,
15917 cmd,
15918 &beforeStmts,
15919 &afterStmts);
15923 }
15924 else if (IsA(stmt, CreateStatsStmt))
15928 cmd));
15929 else
15931 }
15932
15933 /* Caller should already have acquired whatever lock we need. */
15935
15936 /*
15937 * Attach each generated command to the proper place in the work queue.
15938 * Note this could result in creation of entirely new work-queue entries.
15939 *
15940 * Also note that we have to tweak the command subtypes, because it turns
15941 * out that re-creation of indexes and constraints has to act a bit
15942 * differently from initial creation.
15943 */
15944 foreach(list_item, querytree_list)
15945 {
15946 Node *stm = (Node *) lfirst(list_item);
15947 AlteredTableInfo *tab;
15948
15949 tab = ATGetQueueEntry(wqueue, rel);
15950
15951 if (IsA(stm, IndexStmt))
15952 {
15953 IndexStmt *stmt = (IndexStmt *) stm;
15955
15956 if (!rewrite)
15958 stmt->reset_default_tblspc = true;
15959 /* keep the index's comment */
15960 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15961
15963 newcmd->subtype = AT_ReAddIndex;
15964 newcmd->def = (Node *) stmt;
15967 }
15968 else if (IsA(stm, AlterTableStmt))
15969 {
15971 ListCell *lcmd;
15972
15973 foreach(lcmd, stmt->cmds)
15974 {
15976
15977 if (cmd->subtype == AT_AddIndex)
15978 {
15980 Oid indoid;
15981
15982 indstmt = castNode(IndexStmt, cmd->def);
15983 indoid = get_constraint_index(oldId);
15984
15985 if (!rewrite)
15986 TryReuseIndex(indoid, indstmt);
15987 /* keep any comment on the index */
15988 indstmt->idxcomment = GetComment(indoid,
15990 indstmt->reset_default_tblspc = true;
15991
15992 cmd->subtype = AT_ReAddIndex;
15994 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15995
15996 /* recreate any comment on the constraint */
15999 oldId,
16000 rel,
16001 NIL,
16002 indstmt->idxname);
16003 }
16004 else if (cmd->subtype == AT_AddConstraint)
16005 {
16006 Constraint *con = castNode(Constraint, cmd->def);
16007
16009 /* rewriting neither side of a FK */
16010 if (con->contype == CONSTR_FOREIGN &&
16011 !rewrite && tab->rewrite == 0)
16013 con->reset_default_tblspc = true;
16017
16018 /*
16019 * Recreate any comment on the constraint. If we have
16020 * recreated a primary key, then transformTableConstraint
16021 * has added an unnamed not-null constraint here; skip
16022 * this in that case.
16023 */
16024 if (con->conname)
16027 oldId,
16028 rel,
16029 NIL,
16030 con->conname);
16031 else
16032 Assert(con->contype == CONSTR_NOTNULL);
16033 }
16034 else
16035 elog(ERROR, "unexpected statement subtype: %d",
16036 (int) cmd->subtype);
16037 }
16038 }
16039 else if (IsA(stm, AlterDomainStmt))
16040 {
16042
16043 if (stmt->subtype == AD_AddConstraint)
16044 {
16045 Constraint *con = castNode(Constraint, stmt->def);
16047
16049 cmd->def = (Node *) stmt;
16052
16053 /* recreate any comment on the constraint */
16056 oldId,
16057 NULL,
16058 stmt->typeName,
16059 con->conname);
16060 }
16061 else
16062 elog(ERROR, "unexpected statement subtype: %d",
16063 (int) stmt->subtype);
16064 }
16065 else if (IsA(stm, CreateStatsStmt))
16066 {
16069
16070 /* keep the statistics object's comment */
16071 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
16072
16074 newcmd->subtype = AT_ReAddStatistics;
16075 newcmd->def = (Node *) stmt;
16076 tab->subcmds[AT_PASS_MISC] =
16078 }
16079 else
16080 elog(ERROR, "unexpected statement type: %d",
16081 (int) nodeTag(stm));
16082 }
16083
16084 relation_close(rel, NoLock);
16085}
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:1331
#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 7283 of file tablecmds.c.

7286{
7287 if (rel->rd_rel->reloftype && !recursing)
7288 ereport(ERROR,
7290 errmsg("cannot add column to typed table")));
7291
7292 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7293 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7294
7295 if (recurse && !is_view)
7296 cmd->recurse = true;
7297}
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:6981

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

Referenced by ATPrepCmd().

◆ ATPrepAddPrimaryKey()

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

Definition at line 9576 of file tablecmds.c.

9579{
9581 List *children = NIL;
9582 bool got_children = false;
9583
9585 if (pkconstr->contype != CONSTR_PRIMARY)
9586 return;
9587
9588 /* Verify that columns are not-null, or request that they be made so */
9590 {
9593 HeapTuple tuple;
9594
9595 /*
9596 * First check if a suitable constraint exists. If it does, we don't
9597 * need to request another one. We do need to bail out if it's not
9598 * valid, though.
9599 */
9601 if (tuple != NULL)
9602 {
9604
9605 /* All good with this one; don't request another */
9606 heap_freetuple(tuple);
9607 continue;
9608 }
9609 else if (!recurse)
9610 {
9611 /*
9612 * No constraint on this column. Asked not to recurse, we won't
9613 * create one here, but verify that all children have one.
9614 */
9615 if (!got_children)
9616 {
9618 lockmode);
9619 /* only search for children on the first time through */
9620 got_children = true;
9621 }
9622
9623 foreach_oid(childrelid, children)
9624 {
9625 HeapTuple tup;
9626
9628 if (!tup)
9629 ereport(ERROR,
9630 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9632 /* verify it's good enough */
9634 }
9635 }
9636
9637 /* This column is not already not-null, so add it to the queue */
9639
9641 newcmd->subtype = AT_AddConstraint;
9642 /* note we force recurse=true here; see above */
9643 newcmd->recurse = true;
9644 newcmd->def = (Node *) nnconstr;
9645
9646 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9647 }
9648}
Definition value.h:64
static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
Definition tablecmds.c:9655

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

14634{
14635 char *colName = cmd->name;
14636 ColumnDef *def = (ColumnDef *) cmd->def;
14637 TypeName *typeName = def->typeName;
14638 Node *transform = def->cooked_default;
14639 HeapTuple tuple;
14642 Oid targettype;
14643 int32 targettypmod;
14646 ParseState *pstate = make_parsestate(NULL);
14648 bool is_expr;
14649
14650 pstate->p_sourcetext = context->queryString;
14651
14652 if (rel->rd_rel->reloftype && !recursing)
14653 ereport(ERROR,
14655 errmsg("cannot alter column type of typed table"),
14656 parser_errposition(pstate, def->location)));
14657
14658 /* lookup the attribute so we can check inheritance status */
14660 if (!HeapTupleIsValid(tuple))
14661 ereport(ERROR,
14663 errmsg("column \"%s\" of relation \"%s\" does not exist",
14665 parser_errposition(pstate, def->location)));
14667 attnum = attTup->attnum;
14668
14669 /* Can't alter a system attribute */
14670 if (attnum <= 0)
14671 ereport(ERROR,
14673 errmsg("cannot alter system column \"%s\"", colName),
14674 parser_errposition(pstate, def->location)));
14675
14676 /*
14677 * Cannot specify USING when altering type of a generated column, because
14678 * that would violate the generation expression.
14679 */
14680 if (attTup->attgenerated && def->cooked_default)
14681 ereport(ERROR,
14683 errmsg("cannot specify USING when altering type of generated column"),
14684 errdetail("Column \"%s\" is a generated column.", colName),
14685 parser_errposition(pstate, def->location)));
14686
14687 /*
14688 * Don't alter inherited columns. At outer level, there had better not be
14689 * any inherited definition; when recursing, we assume this was checked at
14690 * the parent level (see below).
14691 */
14692 if (attTup->attinhcount > 0 && !recursing)
14693 ereport(ERROR,
14695 errmsg("cannot alter inherited column \"%s\"", colName),
14696 parser_errposition(pstate, def->location)));
14697
14698 /* Don't alter columns used in the partition key */
14699 if (has_partition_attrs(rel,
14701 &is_expr))
14702 ereport(ERROR,
14704 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14706 parser_errposition(pstate, def->location)));
14707
14708 /* Look up the target type */
14709 typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14710
14712 if (aclresult != ACLCHECK_OK)
14713 aclcheck_error_type(aclresult, targettype);
14714
14715 /* And the collation */
14716 targetcollid = GetColumnDefCollation(pstate, def, targettype);
14717
14718 /* make sure datatype is legal for a column */
14720 list_make1_oid(rel->rd_rel->reltype),
14721 (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14722
14723 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14724 {
14725 /* do nothing */
14726 }
14727 else if (tab->relkind == RELKIND_RELATION ||
14729 {
14730 /*
14731 * Set up an expression to transform the old data value to the new
14732 * type. If a USING option was given, use the expression as
14733 * transformed by transformAlterTableStmt, else just take the old
14734 * value and try to coerce it. We do this first so that type
14735 * incompatibility can be detected before we waste effort, and because
14736 * we need the expression to be parsed against the original table row
14737 * type.
14738 */
14739 if (!transform)
14740 {
14741 transform = (Node *) makeVar(1, attnum,
14742 attTup->atttypid, attTup->atttypmod,
14743 attTup->attcollation,
14744 0);
14745 }
14746
14747 transform = coerce_to_target_type(pstate,
14748 transform, exprType(transform),
14749 targettype, targettypmod,
14752 -1);
14753 if (transform == NULL)
14754 {
14755 /* error text depends on whether USING was specified or not */
14756 if (def->cooked_default != NULL)
14757 ereport(ERROR,
14759 errmsg("result of USING clause for column \"%s\""
14760 " cannot be cast automatically to type %s",
14761 colName, format_type_be(targettype)),
14762 errhint("You might need to add an explicit cast.")));
14763 else
14764 ereport(ERROR,
14766 errmsg("column \"%s\" cannot be cast automatically to type %s",
14767 colName, format_type_be(targettype)),
14768 !attTup->attgenerated ?
14769 /* translator: USING is SQL, don't translate it */
14770 errhint("You might need to specify \"USING %s::%s\".",
14772 format_type_with_typemod(targettype,
14773 targettypmod)) : 0));
14774 }
14775
14776 /* Fix collations after all else */
14777 assign_expr_collations(pstate, transform);
14778
14779 /* Expand virtual generated columns in the expr. */
14780 transform = expand_generated_columns_in_expr(transform, rel, 1);
14781
14782 /* Plan the expr now so we can accurately assess the need to rewrite. */
14783 transform = (Node *) expression_planner((Expr *) transform);
14784
14785 /*
14786 * Add a work queue item to make ATRewriteTable update the column
14787 * contents.
14788 */
14790 newval->attnum = attnum;
14791 newval->expr = (Expr *) transform;
14792 newval->is_generated = false;
14793
14794 tab->newvals = lappend(tab->newvals, newval);
14795 if (ATColumnChangeRequiresRewrite(transform, attnum))
14797 }
14798 else if (transform)
14799 ereport(ERROR,
14801 errmsg("\"%s\" is not a table",
14803
14804 if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14805 {
14806 /*
14807 * For relations or columns without storage, do this check now.
14808 * Regular tables will check it later when the table is being
14809 * rewritten.
14810 */
14811 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
14812 }
14813
14814 ReleaseSysCache(tuple);
14815
14816 /*
14817 * Recurse manually by queueing a new command for each child, if
14818 * necessary. We cannot apply ATSimpleRecursion here because we need to
14819 * remap attribute numbers in the USING expression, if any.
14820 *
14821 * If we are told not to recurse, there had better not be any child
14822 * tables; else the alter would put them out of step.
14823 */
14824 if (recurse)
14825 {
14826 Oid relid = RelationGetRelid(rel);
14829 ListCell *lo,
14830 *li;
14831
14832 child_oids = find_all_inheritors(relid, lockmode,
14834
14835 /*
14836 * find_all_inheritors does the recursive search of the inheritance
14837 * hierarchy, so all we have to do is process all of the relids in the
14838 * list that it returns.
14839 */
14841 {
14843 int numparents = lfirst_int(li);
14847
14848 if (childrelid == relid)
14849 continue;
14850
14851 /* find_all_inheritors already got lock */
14854
14855 /*
14856 * Verify that the child doesn't have any inherited definitions of
14857 * this column that came from outside this inheritance hierarchy.
14858 * (renameatt makes a similar test, though in a different way
14859 * because of its different recursion mechanism.)
14860 */
14862 colName);
14864 ereport(ERROR,
14866 errmsg("column \"%s\" of relation \"%s\" does not exist",
14869
14870 if (childattTup->attinhcount > numparents)
14871 ereport(ERROR,
14873 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
14875
14877
14878 /*
14879 * Remap the attribute numbers. If no USING expression was
14880 * specified, there is no need for this step.
14881 */
14882 if (def->cooked_default)
14883 {
14884 AttrMap *attmap;
14885 bool found_whole_row;
14886
14887 /* create a copy to scribble on */
14888 cmd = copyObject(cmd);
14889
14891 RelationGetDescr(rel),
14892 false);
14893 ((ColumnDef *) cmd->def)->cooked_default =
14894 map_variable_attnos(def->cooked_default,
14895 1, 0,
14896 attmap,
14897 InvalidOid, &found_whole_row);
14898 if (found_whole_row)
14899 ereport(ERROR,
14901 errmsg("cannot convert whole-row table reference"),
14902 errdetail("USING expression contains a whole-row table reference.")));
14903 pfree(attmap);
14904 }
14905 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
14907 }
14908 }
14909 else if (!recursing &&
14911 ereport(ERROR,
14913 errmsg("type of inherited column \"%s\" must be changed in child tables too",
14914 colName)));
14915
14916 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
14917 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
14918}
void aclcheck_error_type(AclResult aclerr, Oid typeOid)
Definition aclchk.c:2997
#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 * 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:7026

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

◆ ATPrepChangeInherit()

static void ATPrepChangeInherit ( Relation  child_rel)
static

Definition at line 17494 of file tablecmds.c.

17495{
17496 if (child_rel->rd_rel->reloftype)
17497 ereport(ERROR,
17499 errmsg("cannot change inheritance of typed table")));
17500
17501 if (child_rel->rd_rel->relispartition)
17502 ereport(ERROR,
17504 errmsg("cannot change inheritance of a partition")));
17505}

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

Referenced by ATPrepCmd().

◆ ATPrepChangePersistence()

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

Definition at line 19069 of file tablecmds.c.

19070{
19072 HeapTuple tuple;
19073 SysScanDesc scan;
19074 ScanKeyData skey[1];
19075
19076 /*
19077 * Disallow changing status for a temp table. Also verify whether we can
19078 * get away with doing nothing; in such cases we don't need to run the
19079 * checks below, either.
19080 */
19081 switch (rel->rd_rel->relpersistence)
19082 {
19084 ereport(ERROR,
19086 errmsg("cannot change logged status of table \"%s\" because it is temporary",
19088 errtable(rel)));
19089 break;
19091 if (toLogged)
19092 /* nothing to do */
19093 return;
19094 break;
19096 if (!toLogged)
19097 /* nothing to do */
19098 return;
19099 break;
19100 }
19101
19102 /*
19103 * Check that the table is not part of any publication when changing to
19104 * UNLOGGED, as UNLOGGED tables can't be published.
19105 */
19106 if (!toLogged &&
19108 ereport(ERROR,
19110 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
19112 errdetail("Unlogged relations cannot be replicated.")));
19113
19114 /*
19115 * Check existing foreign key constraints to preserve the invariant that
19116 * permanent tables cannot reference unlogged ones. Self-referencing
19117 * foreign keys can safely be ignored.
19118 */
19120
19121 /*
19122 * Scan conrelid if changing to permanent, else confrelid. This also
19123 * determines whether a useful index exists.
19124 */
19125 ScanKeyInit(&skey[0],
19132 true, NULL, 1, skey);
19133
19134 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19135 {
19137
19138 if (con->contype == CONSTRAINT_FOREIGN)
19139 {
19141 Relation foreignrel;
19142
19143 /* the opposite end of what we used as scankey */
19144 foreignrelid = toLogged ? con->confrelid : con->conrelid;
19145
19146 /* ignore if self-referencing */
19147 if (RelationGetRelid(rel) == foreignrelid)
19148 continue;
19149
19151
19152 if (toLogged)
19153 {
19154 if (!RelationIsPermanent(foreignrel))
19155 ereport(ERROR,
19157 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
19159 RelationGetRelationName(foreignrel)),
19160 errtableconstraint(rel, NameStr(con->conname))));
19161 }
19162 else
19163 {
19164 if (RelationIsPermanent(foreignrel))
19165 ereport(ERROR,
19167 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
19169 RelationGetRelationName(foreignrel)),
19170 errtableconstraint(rel, NameStr(con->conname))));
19171 }
19172
19173 relation_close(foreignrel, AccessShareLock);
19174 }
19175 }
19176
19177 systable_endscan(scan);
19178
19180
19181 /* force rewrite if necessary; see comment in ATRewriteTables */
19183 if (toLogged)
19185 else
19187 tab->chgPersistence = true;
19188}
#define AT_REWRITE_ALTER_PERSISTENCE
return true
Definition isn.c:130
int errtableconstraint(Relation rel, const char *conname)
Definition relcache.c:6127
int errtable(Relation rel)
Definition relcache.c:6073

References AccessShareLock, AT_REWRITE_ALTER_PERSISTENCE, BTEqualStrategyNumber, AlteredTableInfo::chgPersistence, ereport, errcode(), errdetail(), errmsg, ERROR, errtable(), errtableconstraint(), fb(), Form_pg_constraint, GetRelationIncludedPublications(), 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 4967 of file tablecmds.c.

4970{
4971 AlteredTableInfo *tab;
4973
4974 /* Find or create work queue entry for this table */
4975 tab = ATGetQueueEntry(wqueue, rel);
4976
4977 /*
4978 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4979 * partitions that are pending detach.
4980 */
4981 if (rel->rd_rel->relispartition &&
4984 ereport(ERROR,
4986 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4988 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4989
4990 /*
4991 * Copy the original subcommand for each table, so we can scribble on it.
4992 * This avoids conflicts when different child tables need to make
4993 * different parse transformations (for example, the same column may have
4994 * different column numbers in different children).
4995 */
4996 cmd = copyObject(cmd);
4997
4998 /*
4999 * Do permissions and relkind checking, recursion to child tables if
5000 * needed, and any additional phase-1 processing needed. (But beware of
5001 * adding any processing that looks at table details that another
5002 * subcommand could change. In some cases we reject multiple subcommands
5003 * that could try to change the same state in contrary ways.)
5004 */
5005 switch (cmd->subtype)
5006 {
5007 case AT_AddColumn: /* ADD COLUMN */
5008 ATSimplePermissions(cmd->subtype, rel,
5011 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
5012 lockmode, context);
5013 /* Recursion occurs during execution phase */
5015 break;
5016 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5018 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
5019 lockmode, context);
5020 /* Recursion occurs during execution phase */
5022 break;
5023 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5024
5025 /*
5026 * We allow defaults on views so that INSERT into a view can have
5027 * default-ish behavior. This works because the rewriter
5028 * substitutes default values into INSERTs before it expands
5029 * rules.
5030 */
5031 ATSimplePermissions(cmd->subtype, rel,
5034 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5035 /* No command-specific prep needed */
5037 break;
5038 case AT_CookedColumnDefault: /* add a pre-cooked default */
5039 /* This is currently used only in CREATE TABLE */
5040 /* (so the permission check really isn't necessary) */
5041 ATSimplePermissions(cmd->subtype, rel,
5043 /* This command never recurses */
5045 break;
5046 case AT_AddIdentity:
5047 ATSimplePermissions(cmd->subtype, rel,
5050 /* Set up recursion for phase 2; no other prep needed */
5051 if (recurse)
5052 cmd->recurse = true;
5054 break;
5055 case AT_SetIdentity:
5056 ATSimplePermissions(cmd->subtype, rel,
5059 /* Set up recursion for phase 2; no other prep needed */
5060 if (recurse)
5061 cmd->recurse = true;
5062 /* This should run after AddIdentity, so do it in MISC pass */
5064 break;
5065 case AT_DropIdentity:
5066 ATSimplePermissions(cmd->subtype, rel,
5069 /* Set up recursion for phase 2; no other prep needed */
5070 if (recurse)
5071 cmd->recurse = true;
5073 break;
5074 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5075 ATSimplePermissions(cmd->subtype, rel,
5077 /* Set up recursion for phase 2; no other prep needed */
5078 if (recurse)
5079 cmd->recurse = true;
5081 break;
5082 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5083 ATSimplePermissions(cmd->subtype, rel,
5085 /* Set up recursion for phase 2; no other prep needed */
5086 if (recurse)
5087 cmd->recurse = true;
5089 break;
5090 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5091 ATSimplePermissions(cmd->subtype, rel,
5093 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5095 break;
5096 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5097 ATSimplePermissions(cmd->subtype, rel,
5099 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5100 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5102 break;
5103 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5104 ATSimplePermissions(cmd->subtype, rel,
5107 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5108 /* No command-specific prep needed */
5110 break;
5111 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5112 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5113 ATSimplePermissions(cmd->subtype, rel,
5116 /* This command never recurses */
5118 break;
5119 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5120 ATSimplePermissions(cmd->subtype, rel,
5123 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5124 /* No command-specific prep needed */
5126 break;
5127 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5128 ATSimplePermissions(cmd->subtype, rel,
5130 /* This command never recurses */
5131 /* No command-specific prep needed */
5133 break;
5134 case AT_DropColumn: /* DROP COLUMN */
5135 ATSimplePermissions(cmd->subtype, rel,
5138 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5139 lockmode, context);
5140 /* Recursion occurs during execution phase */
5142 break;
5143 case AT_AddIndex: /* ADD INDEX */
5145 /* This command never recurses */
5146 /* No command-specific prep needed */
5148 break;
5149 case AT_AddConstraint: /* ADD CONSTRAINT */
5150 ATSimplePermissions(cmd->subtype, rel,
5152 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5153 if (recurse)
5154 {
5155 /* recurses at exec time; lock descendants and set flag */
5156 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5157 cmd->recurse = true;
5158 }
5160 break;
5161 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5163 /* This command never recurses */
5164 /* No command-specific prep needed */
5166 break;
5167 case AT_DropConstraint: /* DROP CONSTRAINT */
5168 ATSimplePermissions(cmd->subtype, rel,
5170 ATCheckPartitionsNotInUse(rel, lockmode);
5171 /* Other recursion occurs during execution phase */
5172 /* No command-specific prep needed except saving recurse flag */
5173 if (recurse)
5174 cmd->recurse = true;
5176 break;
5177 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5178 ATSimplePermissions(cmd->subtype, rel,
5181 /* See comments for ATPrepAlterColumnType */
5182 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5183 AT_PASS_UNSET, context);
5184 Assert(cmd != NULL);
5185 /* Performs own recursion */
5186 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5187 lockmode, context);
5189 break;
5192 /* This command never recurses */
5193 /* No command-specific prep needed */
5195 break;
5196 case AT_ChangeOwner: /* ALTER OWNER */
5197 /* This command never recurses */
5198 /* No command-specific prep needed */
5200 break;
5201 case AT_ClusterOn: /* CLUSTER ON */
5202 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5203 ATSimplePermissions(cmd->subtype, rel,
5205 /* These commands never recurse */
5206 /* No command-specific prep needed */
5208 break;
5209 case AT_SetLogged: /* SET LOGGED */
5210 case AT_SetUnLogged: /* SET UNLOGGED */
5212 if (tab->chgPersistence)
5213 ereport(ERROR,
5215 errmsg("cannot change persistence setting twice")));
5216 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5218 break;
5219 case AT_DropOids: /* SET WITHOUT OIDS */
5220 ATSimplePermissions(cmd->subtype, rel,
5223 break;
5224 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5225 ATSimplePermissions(cmd->subtype, rel,
5227
5228 /* check if another access method change was already requested */
5229 if (tab->chgAccessMethod)
5230 ereport(ERROR,
5232 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5233
5234 ATPrepSetAccessMethod(tab, rel, cmd->name);
5235 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5236 break;
5237 case AT_SetTableSpace: /* SET TABLESPACE */
5240 /* This command never recurses */
5241 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5242 pass = AT_PASS_MISC; /* doesn't actually matter */
5243 break;
5244 case AT_SetRelOptions: /* SET (...) */
5245 case AT_ResetRelOptions: /* RESET (...) */
5246 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5247 ATSimplePermissions(cmd->subtype, rel,
5250 /* This command never recurses */
5251 /* No command-specific prep needed */
5253 break;
5254 case AT_AddInherit: /* INHERIT */
5255 ATSimplePermissions(cmd->subtype, rel,
5257 /* This command never recurses */
5260 break;
5261 case AT_DropInherit: /* NO INHERIT */
5262 ATSimplePermissions(cmd->subtype, rel,
5264 /* This command never recurses */
5267 break;
5268 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5269 ATSimplePermissions(cmd->subtype, rel,
5271 /* Recursion occurs during execution phase */
5272 if (recurse)
5273 cmd->recurse = true;
5275 break;
5276 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5277 ATSimplePermissions(cmd->subtype, rel,
5279 /* Recursion occurs during execution phase */
5280 /* No command-specific prep needed except saving recurse flag */
5281 if (recurse)
5282 cmd->recurse = true;
5284 break;
5285 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5286 ATSimplePermissions(cmd->subtype, rel,
5289 /* This command never recurses */
5290 /* No command-specific prep needed */
5291 break;
5292 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5295 case AT_EnableTrigAll:
5296 case AT_EnableTrigUser:
5297 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5298 case AT_DisableTrigAll:
5299 case AT_DisableTrigUser:
5300 ATSimplePermissions(cmd->subtype, rel,
5302 /* Set up recursion for phase 2; no other prep needed */
5303 if (recurse)
5304 cmd->recurse = true;
5306 break;
5307 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5310 case AT_DisableRule:
5311 case AT_AddOf: /* OF */
5312 case AT_DropOf: /* NOT OF */
5317 ATSimplePermissions(cmd->subtype, rel,
5319 /* These commands never recurse */
5320 /* No command-specific prep needed */
5322 break;
5323 case AT_GenericOptions:
5325 /* No command-specific prep needed */
5327 break;
5328 case AT_AttachPartition:
5329 ATSimplePermissions(cmd->subtype, rel,
5331 /* No command-specific prep needed */
5333 break;
5334 case AT_DetachPartition:
5336 /* No command-specific prep needed */
5338 break;
5341 /* No command-specific prep needed */
5343 break;
5344 case AT_MergePartitions:
5345 case AT_SplitPartition:
5347 /* No command-specific prep needed */
5349 break;
5350 default: /* oops */
5351 elog(ERROR, "unrecognized alter table type: %d",
5352 (int) cmd->subtype);
5353 pass = AT_PASS_UNSET; /* keep compiler quiet */
5354 break;
5355 }
5357
5358 /* Add the subcommand to the appropriate list for phase 2 */
5359 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5360}
bool PartitionHasPendingDetach(Oid partoid)
static void ATPrepChangeInherit(Relation child_rel)
#define ATT_SEQUENCE
Definition tablecmds.c:345
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition tablecmds.c:6951
#define ATT_INDEX
Definition tablecmds.c:341
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition tablecmds.c:8833
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:9333
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:7283
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:6906
#define ATT_PARTITIONED_INDEX
Definition tablecmds.c:344
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:9576
#define ATT_VIEW
Definition tablecmds.c:339
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:342
#define ATT_MATVIEW
Definition tablecmds.c:340
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(), ATPrepAddPrimaryKey(), ATPrepAlterColumnType(), ATPrepChangeInherit(), 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 9333 of file tablecmds.c.

9336{
9337 if (rel->rd_rel->reloftype && !recursing)
9338 ereport(ERROR,
9340 errmsg("cannot drop column from typed table")));
9341
9342 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9343 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9344
9345 if (recurse)
9346 cmd->recurse = true;
9347}

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

8834{
8835 /*
8836 * Reject ONLY if there are child tables. We could implement this, but it
8837 * is a bit complicated. GENERATED clauses must be attached to the column
8838 * definition and cannot be added later like DEFAULT, so if a child table
8839 * has a generation expression that the parent does not have, the child
8840 * column will necessarily be an attislocal column. So to implement ONLY
8841 * here, we'd need extra code to update attislocal of the direct child
8842 * tables, somewhat similar to how DROP COLUMN does it, so that the
8843 * resulting state can be properly dumped and restored.
8844 */
8845 if (!recurse &&
8847 ereport(ERROR,
8849 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8850
8851 /*
8852 * Cannot drop generation expression from inherited columns.
8853 */
8854 if (!recursing)
8855 {
8856 HeapTuple tuple;
8858
8860 if (!HeapTupleIsValid(tuple))
8861 ereport(ERROR,
8863 errmsg("column \"%s\" of relation \"%s\" does not exist",
8864 cmd->name, RelationGetRelationName(rel))));
8865
8867
8868 if (attTup->attinhcount > 0)
8869 ereport(ERROR,
8871 errmsg("cannot drop generation expression from inherited column")));
8872 }
8873}

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

16749{
16750 Oid amoid;
16751
16752 /*
16753 * Look up the access method name and check that it differs from the
16754 * table's current AM. If DEFAULT was specified for a partitioned table
16755 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16756 */
16757 if (amname != NULL)
16758 amoid = get_table_am_oid(amname, false);
16759 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16760 amoid = InvalidOid;
16761 else
16763
16764 /* if it's a match, phase 3 doesn't need to do anything */
16765 if (rel->rd_rel->relam == amoid)
16766 return;
16767
16768 /* Save info for Phase 3 to do the real work */
16770 tab->newAccessMethod = amoid;
16771 tab->chgAccessMethod = true;
16772}
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 16872 of file tablecmds.c.

16873{
16875
16876 /* Check that the tablespace exists */
16877 tablespaceId = get_tablespace_oid(tablespacename, false);
16878
16879 /* Check permissions except when moving to database's default */
16881 {
16883
16885 if (aclresult != ACLCHECK_OK)
16886 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16887 }
16888
16889 /* Save info for Phase 3 to do the real work */
16890 if (OidIsValid(tab->newTableSpace))
16891 ereport(ERROR,
16893 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16894
16896}

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

5372{
5373 ListCell *ltab;
5374
5375 /*
5376 * We process all the tables "in parallel", one pass at a time. This is
5377 * needed because we may have to propagate work from one table to another
5378 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5379 * re-adding of the foreign key constraint to the other table). Work can
5380 * only be propagated into later passes, however.
5381 */
5382 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5383 {
5384 /* Go through each table that needs to be processed */
5385 foreach(ltab, *wqueue)
5386 {
5388 List *subcmds = tab->subcmds[pass];
5389 ListCell *lcmd;
5390
5391 if (subcmds == NIL)
5392 continue;
5393
5394 /*
5395 * Open the relation and store it in tab. This allows subroutines
5396 * close and reopen, if necessary. Appropriate lock was obtained
5397 * by phase 1, needn't get it again.
5398 */
5399 tab->rel = relation_open(tab->relid, NoLock);
5400
5401 foreach(lcmd, subcmds)
5402 ATExecCmd(wqueue, tab,
5404 lockmode, pass, context);
5405
5406 /*
5407 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5408 * (this is not done in ATExecAlterColumnType since it should be
5409 * done only once if multiple columns of a table are altered).
5410 */
5412 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5413
5414 if (tab->rel)
5415 {
5416 relation_close(tab->rel, NoLock);
5417 tab->rel = NULL;
5418 }
5419 }
5420 }
5421
5422 /* Check to see if a toast table must be added. */
5423 foreach(ltab, *wqueue)
5424 {
5426
5427 /*
5428 * If the table is source table of ATTACH PARTITION command, we did
5429 * not modify anything about it that will change its toasting
5430 * requirement, so no need to check.
5431 */
5432 if (((tab->relkind == RELKIND_RELATION ||
5434 tab->partition_constraint == NULL) ||
5435 tab->relkind == RELKIND_MATVIEW)
5436 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5437 }
5438}
Expr * partition_constraint
Definition tablecmds.c:202
#define AT_NUM_PASSES
Definition tablecmds.c:170
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:5444
void AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
Definition toasting.c:59

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

6212{
6217 bool needscan = false;
6220 int i;
6221 ListCell *l;
6222 EState *estate;
6223 CommandId mycid;
6224 BulkInsertState bistate;
6225 uint32 ti_options;
6226 ExprState *partqualstate = NULL;
6227
6228 /*
6229 * Open the relation(s). We have surely already locked the existing
6230 * table.
6231 */
6232 oldrel = table_open(tab->relid, NoLock);
6233 oldTupDesc = tab->oldDesc;
6234 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6235
6237 {
6239 false));
6241 }
6242 else
6243 newrel = NULL;
6244
6245 /*
6246 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6247 * is empty, so don't bother using it.
6248 */
6249 if (newrel)
6250 {
6251 mycid = GetCurrentCommandId(true);
6252 bistate = GetBulkInsertState();
6253 ti_options = TABLE_INSERT_SKIP_FSM;
6254 }
6255 else
6256 {
6257 /* keep compiler quiet about using these uninitialized */
6258 mycid = 0;
6259 bistate = NULL;
6260 ti_options = 0;
6261 }
6262
6263 /*
6264 * Generate the constraint and default execution states
6265 */
6266
6267 estate = CreateExecutorState();
6268
6269 /* Build the needed expression execution states */
6270 foreach(l, tab->constraints)
6271 {
6272 NewConstraint *con = lfirst(l);
6273
6274 switch (con->contype)
6275 {
6276 case CONSTR_CHECK:
6277 needscan = true;
6279 break;
6280 case CONSTR_FOREIGN:
6281 /* Nothing to do here */
6282 break;
6283 default:
6284 elog(ERROR, "unrecognized constraint type: %d",
6285 (int) con->contype);
6286 }
6287 }
6288
6289 /* Build expression execution states for partition check quals */
6290 if (tab->partition_constraint)
6291 {
6292 needscan = true;
6293 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6294 }
6295
6296 foreach(l, tab->newvals)
6297 {
6298 NewColumnValue *ex = lfirst(l);
6299
6300 /* expr already planned */
6301 ex->exprstate = ExecInitExpr(ex->expr, NULL);
6302 }
6303
6305 if (newrel || tab->verify_new_notnull)
6306 {
6307 /*
6308 * If we are rebuilding the tuples OR if we added any new but not
6309 * verified not-null constraints, check all *valid* not-null
6310 * constraints. This is a bit of overkill but it minimizes risk of
6311 * bugs.
6312 *
6313 * notnull_attrs does *not* collect attribute numbers for valid
6314 * not-null constraints over virtual generated columns; instead, they
6315 * are collected in notnull_virtual_attrs for verification elsewhere.
6316 */
6317 for (i = 0; i < newTupDesc->natts; i++)
6318 {
6320
6321 if (attr->attnullability == ATTNULLABLE_VALID &&
6322 !attr->attisdropped)
6323 {
6325
6326 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6328 else
6330 wholeatt->attnum);
6331 }
6332 }
6334 needscan = true;
6335 }
6336
6337 if (newrel || needscan)
6338 {
6339 ExprContext *econtext;
6342 TableScanDesc scan;
6345 ListCell *lc;
6346 Snapshot snapshot;
6348
6349 /*
6350 * When adding or changing a virtual generated column with a not-null
6351 * constraint, we need to evaluate whether the generation expression
6352 * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6353 * prepare a dummy ResultRelInfo.
6354 */
6356 {
6357 MemoryContext oldcontext;
6358
6359 Assert(newTupDesc->constr->has_generated_virtual);
6360 Assert(newTupDesc->constr->has_not_null);
6361 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6364 oldrel,
6365 0, /* dummy rangetable index */
6366 NULL,
6367 estate->es_instrument);
6368 MemoryContextSwitchTo(oldcontext);
6369 }
6370
6371 if (newrel)
6373 (errmsg_internal("rewriting table \"%s\"",
6375 else
6377 (errmsg_internal("verifying table \"%s\"",
6379
6380 if (newrel)
6381 {
6382 /*
6383 * All predicate locks on the tuples or pages are about to be made
6384 * invalid, because we move tuples around. Promote them to
6385 * relation locks.
6386 */
6388 }
6389
6390 econtext = GetPerTupleExprContext(estate);
6391
6392 /*
6393 * Create necessary tuple slots. When rewriting, two slots are needed,
6394 * otherwise one suffices. In the case where one slot suffices, we
6395 * need to use the new tuple descriptor, otherwise some constraints
6396 * can't be evaluated. Note that even when the tuple layout is the
6397 * same and no rewrite is required, the tupDescs might not be
6398 * (consider ADD COLUMN without a default).
6399 */
6400 if (tab->rewrite)
6401 {
6402 Assert(newrel != NULL);
6407
6408 /*
6409 * Set all columns in the new slot to NULL initially, to ensure
6410 * columns added as part of the rewrite are initialized to NULL.
6411 * That is necessary as tab->newvals will not contain an
6412 * expression for columns with a NULL default, e.g. when adding a
6413 * column without a default together with a column with a default
6414 * requiring an actual rewrite.
6415 */
6417 }
6418 else
6419 {
6422 newslot = NULL;
6423 }
6424
6425 /*
6426 * Any attributes that are dropped according to the new tuple
6427 * descriptor can be set to NULL. We precompute the list of dropped
6428 * attributes to avoid needing to do so in the per-tuple loop.
6429 */
6430 for (i = 0; i < newTupDesc->natts; i++)
6431 {
6432 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6434 }
6435
6436 /*
6437 * Scan through the rows, generating a new row if needed and then
6438 * checking all the constraints.
6439 */
6440 snapshot = RegisterSnapshot(GetLatestSnapshot());
6441 scan = table_beginscan(oldrel, snapshot, 0, NULL,
6442 SO_NONE);
6443
6444 /*
6445 * Switch to per-tuple memory context and reset it for each tuple
6446 * produced, so we don't leak memory.
6447 */
6449
6451 {
6453
6454 if (tab->rewrite > 0)
6455 {
6456 /* Extract data from old tuple */
6459
6460 /* copy attributes */
6461 memcpy(newslot->tts_values, oldslot->tts_values,
6462 sizeof(Datum) * oldslot->tts_nvalid);
6463 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6464 sizeof(bool) * oldslot->tts_nvalid);
6465
6466 /* Set dropped attributes to null in new tuple */
6467 foreach(lc, dropped_attrs)
6468 newslot->tts_isnull[lfirst_int(lc)] = true;
6469
6470 /*
6471 * Constraints and GENERATED expressions might reference the
6472 * tableoid column, so fill tts_tableOid with the desired
6473 * value. (We must do this each time, because it gets
6474 * overwritten with newrel's OID during storing.)
6475 */
6476 newslot->tts_tableOid = RelationGetRelid(oldrel);
6477
6478 /*
6479 * Process supplied expressions to replace selected columns.
6480 *
6481 * First, evaluate expressions whose inputs come from the old
6482 * tuple.
6483 */
6484 econtext->ecxt_scantuple = oldslot;
6485
6486 foreach(l, tab->newvals)
6487 {
6488 NewColumnValue *ex = lfirst(l);
6489
6490 if (ex->is_generated)
6491 continue;
6492
6493 newslot->tts_values[ex->attnum - 1]
6494 = ExecEvalExpr(ex->exprstate,
6495 econtext,
6496 &newslot->tts_isnull[ex->attnum - 1]);
6497 }
6498
6500
6501 /*
6502 * Now, evaluate any expressions whose inputs come from the
6503 * new tuple. We assume these columns won't reference each
6504 * other, so that there's no ordering dependency.
6505 */
6506 econtext->ecxt_scantuple = newslot;
6507
6508 foreach(l, tab->newvals)
6509 {
6510 NewColumnValue *ex = lfirst(l);
6511
6512 if (!ex->is_generated)
6513 continue;
6514
6515 newslot->tts_values[ex->attnum - 1]
6516 = ExecEvalExpr(ex->exprstate,
6517 econtext,
6518 &newslot->tts_isnull[ex->attnum - 1]);
6519 }
6520
6522 }
6523 else
6524 {
6525 /*
6526 * If there's no rewrite, old and new table are guaranteed to
6527 * have the same AM, so we can just use the old slot to verify
6528 * new constraints etc.
6529 */
6531 }
6532
6533 /* Now check any constraints on the possibly-changed tuple */
6534 econtext->ecxt_scantuple = insertslot;
6535
6537 {
6539 {
6541
6542 ereport(ERROR,
6544 errmsg("column \"%s\" of relation \"%s\" contains null values",
6545 NameStr(attr->attname),
6548 }
6549 }
6550
6552 {
6554
6556 estate,
6559 {
6561
6562 ereport(ERROR,
6564 errmsg("column \"%s\" of relation \"%s\" contains null values",
6565 NameStr(attr->attname),
6568 }
6569 }
6570
6571 foreach(l, tab->constraints)
6572 {
6573 NewConstraint *con = lfirst(l);
6574
6575 switch (con->contype)
6576 {
6577 case CONSTR_CHECK:
6578 if (!ExecCheck(con->qualstate, econtext))
6579 ereport(ERROR,
6581 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6582 con->name,
6585 break;
6586 case CONSTR_NOTNULL:
6587 case CONSTR_FOREIGN:
6588 /* Nothing to do here */
6589 break;
6590 default:
6591 elog(ERROR, "unrecognized constraint type: %d",
6592 (int) con->contype);
6593 }
6594 }
6595
6596 if (partqualstate && !ExecCheck(partqualstate, econtext))
6597 {
6598 if (tab->validate_default)
6599 ereport(ERROR,
6601 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6603 errtable(oldrel)));
6604 else
6605 ereport(ERROR,
6607 errmsg("partition constraint of relation \"%s\" is violated by some row",
6609 errtable(oldrel)));
6610 }
6611
6612 /* Write the tuple out to the new relation */
6613 if (newrel)
6615 ti_options, bistate);
6616
6617 ResetExprContext(econtext);
6618
6620 }
6621
6623 table_endscan(scan);
6624 UnregisterSnapshot(snapshot);
6625
6627 if (newslot)
6629 }
6630
6631 FreeExecutorState(estate);
6632
6634 if (newrel)
6635 {
6636 FreeBulkInsertState(bistate);
6637
6638 table_finish_bulk_insert(newrel, ti_options);
6639
6641 }
6642}
uint32_t uint32
Definition c.h:624
uint32 CommandId
Definition c.h:750
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
int int errmsg_internal(const char *fmt,...) pg_attribute_printf(1
#define DEBUG1
Definition elog.h:31
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:1271
AttrNumber ExecRelGenVirtualNotNull(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, List *notnull_virtual_attrs)
Definition execMain.c:2123
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:659
#define GetPerTupleMemoryContext(estate)
Definition executor.h:670
BulkInsertState GetBulkInsertState(void)
Definition heapam.c:1937
void FreeBulkInsertState(BulkInsertState bistate)
Definition heapam.c:1954
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:125
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
#define foreach_int(var, lst)
Definition pg_list.h:502
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition predicate.c:3052
int errtablecol(Relation rel, int attnum)
Definition relcache.c:6090
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:78
char attnullability
Definition tupdesc.h:80
int es_instrument
Definition execnodes.h:757
MemoryContext es_query_cxt
Definition execnodes.h:747
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:287
ConstrType contype
Definition tablecmds.c:221
ExprState * qualstate
Definition tablecmds.c:227
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition tableam.c:59
@ SO_NONE
Definition tableam.h:49
#define TABLE_INSERT_SKIP_FSM
Definition tableam.h:284
static void table_finish_bulk_insert(Relation rel, uint32 options)
Definition tableam.h:1661
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, uint32 options, BulkInsertStateData *bistate)
Definition tableam.h:1458
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key, uint32 flags)
Definition tableam.h:943
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition tableam.h:1096
#define ATTNULLABLE_VALID
Definition tupdesc.h:86
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:195
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
static void slot_getallattrs(TupleTableSlot *slot)
Definition tuptable.h:390
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition tuptable.h:403
CommandId GetCurrentCommandId(bool used)
Definition xact.c:831

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(), memcpy(), 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(), SO_NONE, 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 5922 of file tablecmds.c.

5924{
5925 ListCell *ltab;
5926
5927 /* Go through each table that needs to be checked or rewritten */
5928 foreach(ltab, *wqueue)
5929 {
5931
5932 /* Relations without storage may be ignored here */
5933 if (!RELKIND_HAS_STORAGE(tab->relkind))
5934 continue;
5935
5936 /*
5937 * If we change column data types, the operation has to be propagated
5938 * to tables that use this table's rowtype as a column type.
5939 * tab->newvals will also be non-NULL in the case where we're adding a
5940 * column with a default. We choose to forbid that case as well,
5941 * since composite types might eventually support defaults.
5942 *
5943 * (Eventually we'll probably need to check for composite type
5944 * dependencies even when we're just scanning the table without a
5945 * rewrite, but at the moment a composite type does not enforce any
5946 * constraints, so it's not necessary/appropriate to enforce them just
5947 * during ALTER.)
5948 */
5949 if (tab->newvals != NIL || tab->rewrite > 0)
5950 {
5951 Relation rel;
5952
5953 rel = table_open(tab->relid, NoLock);
5954 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
5955 table_close(rel, NoLock);
5956 }
5957
5958 /*
5959 * We only need to rewrite the table if at least one column needs to
5960 * be recomputed, or we are changing its persistence or access method.
5961 *
5962 * There are two reasons for requiring a rewrite when changing
5963 * persistence: on one hand, we need to ensure that the buffers
5964 * belonging to each of the two relations are marked with or without
5965 * BM_PERMANENT properly. On the other hand, since rewriting creates
5966 * and assigns a new relfilenumber, we automatically create or drop an
5967 * init fork for the relation as appropriate.
5968 */
5969 if (tab->rewrite > 0 && tab->relkind != RELKIND_SEQUENCE)
5970 {
5971 /* Build a temporary relation and copy data */
5976 char persistence;
5977
5978 OldHeap = table_open(tab->relid, NoLock);
5979
5980 /*
5981 * We don't support rewriting of system catalogs; there are too
5982 * many corner cases and too little benefit. In particular this
5983 * is certainly not going to work for mapped catalogs.
5984 */
5986 ereport(ERROR,
5988 errmsg("cannot rewrite system relation \"%s\"",
5990
5992 ereport(ERROR,
5994 errmsg("cannot rewrite table \"%s\" used as a catalog table",
5996
5997 /*
5998 * Don't allow rewrite on temp tables of other backends ... their
5999 * local buffer manager is not going to cope. (This is redundant
6000 * with the check in CheckAlterTableIsSafe, but for safety we'll
6001 * check here too.)
6002 */
6004 ereport(ERROR,
6006 errmsg("cannot rewrite temporary tables of other sessions")));
6007
6008 /*
6009 * Select destination tablespace (same as original unless user
6010 * requested a change)
6011 */
6012 if (tab->newTableSpace)
6014 else
6015 NewTableSpace = OldHeap->rd_rel->reltablespace;
6016
6017 /*
6018 * Select destination access method (same as original unless user
6019 * requested a change)
6020 */
6021 if (tab->chgAccessMethod)
6023 else
6024 NewAccessMethod = OldHeap->rd_rel->relam;
6025
6026 /*
6027 * Select persistence of transient table (same as original unless
6028 * user requested a change)
6029 */
6030 persistence = tab->chgPersistence ?
6031 tab->newrelpersistence : OldHeap->rd_rel->relpersistence;
6032
6034
6035 /*
6036 * Fire off an Event Trigger now, before actually rewriting the
6037 * table.
6038 *
6039 * We don't support Event Trigger for nested commands anywhere,
6040 * here included, and parsetree is given NULL when coming from
6041 * AlterTableInternal.
6042 *
6043 * And fire it only once.
6044 */
6045 if (parsetree)
6046 EventTriggerTableRewrite((Node *) parsetree,
6047 tab->relid,
6048 tab->rewrite);
6049
6050 /*
6051 * Create transient table that will receive the modified data.
6052 *
6053 * Ensure it is marked correctly as logged or unlogged. We have
6054 * to do this here so that buffers for the new relfilenumber will
6055 * have the right persistence set, and at the same time ensure
6056 * that the original filenumbers's buffers will get read in with
6057 * the correct setting (i.e. the original one). Otherwise a
6058 * rollback after the rewrite would possibly result with buffers
6059 * for the original filenumbers having the wrong persistence
6060 * setting.
6061 *
6062 * NB: This relies on swap_relation_files() also swapping the
6063 * persistence. That wouldn't work for pg_class, but that can't be
6064 * unlogged anyway.
6065 */
6067 persistence, lockmode);
6068
6069 /*
6070 * Copy the heap data into the new table with the desired
6071 * modifications, and test the current data within the table
6072 * against new constraints generated by ALTER TABLE commands.
6073 */
6075
6076 /*
6077 * Swap the physical files of the old and new heaps, then rebuild
6078 * indexes and discard the old heap. We can use RecentXmin for
6079 * the table's new relfrozenxid because we rewrote all the tuples
6080 * in ATRewriteTable, so no older Xid remains in the table. Also,
6081 * we never try to swap toast tables by content, since we have no
6082 * interest in letting this code work on system catalogs.
6083 */
6085 false, false, true,
6087 true, /* reindex */
6088 RecentXmin,
6090 persistence);
6091
6093 }
6094 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6095 {
6096 if (tab->chgPersistence)
6098 }
6099 else
6100 {
6101 /*
6102 * If required, test the current data within the table against new
6103 * constraints generated by ALTER TABLE commands, but don't
6104 * rebuild data.
6105 */
6106 if (tab->constraints != NIL || tab->verify_new_notnull ||
6107 tab->partition_constraint != NULL)
6109
6110 /*
6111 * If we had SET TABLESPACE but no reason to reconstruct tuples,
6112 * just do a block-by-block copy.
6113 */
6114 if (tab->newTableSpace)
6115 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6116 }
6117
6118 /*
6119 * Also change persistence of owned sequences, so that it matches the
6120 * table persistence.
6121 */
6122 if (tab->chgPersistence)
6123 {
6125 ListCell *lc;
6126
6127 foreach(lc, seqlist)
6128 {
6130
6132 }
6133 }
6134 }
6135
6136 /*
6137 * Foreign key constraints are checked in a final pass, since (a) it's
6138 * generally best to examine each one separately, and (b) it's at least
6139 * theoretically possible that we have changed both relations of the
6140 * foreign key, and we'd better have finished both rewrites before we try
6141 * to read the tables.
6142 */
6143 foreach(ltab, *wqueue)
6144 {
6146 Relation rel = NULL;
6147 ListCell *lcon;
6148
6149 /* Relations without storage may be ignored here too */
6150 if (!RELKIND_HAS_STORAGE(tab->relkind))
6151 continue;
6152
6153 foreach(lcon, tab->constraints)
6154 {
6155 NewConstraint *con = lfirst(lcon);
6156
6157 if (con->contype == CONSTR_FOREIGN)
6158 {
6161
6162 if (rel == NULL)
6163 {
6164 /* Long since locked, no need for another */
6165 rel = table_open(tab->relid, NoLock);
6166 }
6167
6169
6171 con->refindid,
6172 con->conid,
6173 con->conwithperiod);
6174
6175 /*
6176 * No need to mark the constraint row as validated, we did
6177 * that when we inserted the row earlier.
6178 */
6179
6181 }
6182 }
6183
6184 if (rel)
6185 table_close(rel, NoLock);
6186 }
6187
6188 /* Finally, run any afterStmts that were queued up */
6189 foreach(ltab, *wqueue)
6190 {
6192 ListCell *lc;
6193
6194 foreach(lc, tab->afterStmts)
6195 {
6196 Node *stmt = (Node *) lfirst(lc);
6197
6200 }
6201 }
6202}
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:679
List * getOwnedSequences(Oid relid)
Definition pg_depend.c:1140
#define RelationIsUsedAsCatalogTable(relation)
Definition rel.h:399
void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, bool check_constraints, bool is_internal, bool reindex, TransactionId frozenXid, MultiXactId cutoffMulti, char newrelpersistence)
Definition repack.c:1911
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
Definition repack.c:1154
TransactionId RecentXmin
Definition snapmgr.c:160
bool conwithperiod
Definition tablecmds.c:224
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:6211

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

6830{
6831 int actual_target;
6832
6833 switch (rel->rd_rel->relkind)
6834 {
6835 case RELKIND_RELATION:
6837 break;
6840 break;
6841 case RELKIND_VIEW:
6843 break;
6844 case RELKIND_MATVIEW:
6846 break;
6847 case RELKIND_INDEX:
6849 break;
6852 break;
6855 break;
6858 break;
6859 case RELKIND_SEQUENCE:
6861 break;
6862 default:
6863 actual_target = 0;
6864 break;
6865 }
6866
6867 /* Wrong target type? */
6868 if ((actual_target & allowed_targets) == 0)
6869 {
6870 const char *action_str = alter_table_type_to_string(cmdtype);
6871
6872 if (action_str)
6873 ereport(ERROR,
6875 /* translator: %s is a group of some SQL keywords */
6876 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6878 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6879 else
6880 /* internal error? */
6881 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6883 }
6884
6885 /* Permissions checks */
6889
6891 ereport(ERROR,
6893 errmsg("permission denied: \"%s\" is a system catalog",
6895}
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition tablecmds.c:6682

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

6909{
6910 /*
6911 * Propagate to children, if desired and if there are (or might be) any
6912 * children.
6913 */
6914 if (recurse && rel->rd_rel->relhassubclass)
6915 {
6916 Oid relid = RelationGetRelid(rel);
6917 ListCell *child;
6918 List *children;
6919
6920 children = find_all_inheritors(relid, lockmode, NULL);
6921
6922 /*
6923 * find_all_inheritors does the recursive search of the inheritance
6924 * hierarchy, so all we have to do is process all of the relids in the
6925 * list that it returns.
6926 */
6927 foreach(child, children)
6928 {
6929 Oid childrelid = lfirst_oid(child);
6931
6932 if (childrelid == relid)
6933 continue;
6934 /* find_all_inheritors already got lock */
6937 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6939 }
6940 }
6941}

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

20888{
20889 List *idxes;
20893 ListCell *cell;
20894 MemoryContext cxt;
20896
20898 "AttachPartitionEnsureIndexes",
20901
20906
20907 /* Build arrays of all existing indexes and their IndexInfos */
20909 {
20911
20914 }
20915
20916 /*
20917 * If we're attaching a foreign table, we must fail if any of the indexes
20918 * is a constraint index; otherwise, there's nothing to do here. Do this
20919 * before starting work, to avoid wasting the effort of building a few
20920 * non-unique indexes before coming across a unique one.
20921 */
20922 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20923 {
20924 foreach(cell, idxes)
20925 {
20926 Oid idx = lfirst_oid(cell);
20928
20929 if (idxRel->rd_index->indisunique ||
20930 idxRel->rd_index->indisprimary)
20931 ereport(ERROR,
20933 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20936 errdetail("Partitioned table \"%s\" contains unique indexes.",
20939 }
20940
20941 goto out;
20942 }
20943
20944 /*
20945 * For each index on the partitioned table, find a matching one in the
20946 * partition-to-be; if one is not found, create one.
20947 */
20948 foreach(cell, idxes)
20949 {
20950 Oid idx = lfirst_oid(cell);
20952 IndexInfo *info;
20953 AttrMap *attmap;
20954 bool found = false;
20956
20957 /*
20958 * Ignore indexes in the partitioned table other than partitioned
20959 * indexes.
20960 */
20961 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20962 {
20964 continue;
20965 }
20966
20967 /* construct an indexinfo to compare existing indexes against */
20968 info = BuildIndexInfo(idxRel);
20970 RelationGetDescr(rel),
20971 false);
20973
20974 /*
20975 * Scan the list of existing indexes in the partition-to-be, and mark
20976 * the first matching, valid, unattached one we find, if any, as
20977 * partition of the parent index. If we find one, we're done.
20978 */
20979 for (int i = 0; i < list_length(attachRelIdxs); i++)
20980 {
20983
20984 /* does this index have a parent? if so, can't use it */
20985 if (attachrelIdxRels[i]->rd_rel->relispartition)
20986 continue;
20987
20988 /* If this index is invalid, can't use it */
20989 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20990 continue;
20991
20992 if (CompareIndexInfo(attachInfos[i], info,
20993 attachrelIdxRels[i]->rd_indcollation,
20994 idxRel->rd_indcollation,
20995 attachrelIdxRels[i]->rd_opfamily,
20996 idxRel->rd_opfamily,
20997 attmap))
20998 {
20999 /*
21000 * If this index is being created in the parent because of a
21001 * constraint, then the child needs to have a constraint also,
21002 * so look for one. If there is no such constraint, this
21003 * index is no good, so keep looking.
21004 */
21006 {
21007 cldConstrOid =
21009 cldIdxId);
21010 /* no dice */
21012 continue;
21013
21014 /* Ensure they're both the same type of constraint */
21017 continue;
21018 }
21019
21020 /* bingo. */
21025 found = true;
21026
21028 break;
21029 }
21030 }
21031
21032 /*
21033 * If no suitable index was found in the partition-to-be, create one
21034 * now. Note that if this is a PK, not-null constraints must already
21035 * exist.
21036 */
21037 if (!found)
21038 {
21039 IndexStmt *stmt;
21040 Oid conOid;
21041
21043 idxRel, attmap,
21044 &conOid);
21048 conOid,
21049 -1,
21050 true, false, false, false, false);
21051 }
21052
21054 }
21055
21056out:
21057 /* Clean up. */
21058 for (int i = 0; i < list_length(attachRelIdxs); i++)
21062}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:263
char get_constraint_type(Oid conoid)
Definition lsyscache.c:1361
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
#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:435

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

11875{
11880 bool queueValidation;
11884
11885 /* Fetch the parent constraint tuple */
11889 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11891 parentConstrIsEnforced = parentConstr->conenforced;
11892
11893 /* Fetch the child constraint tuple */
11897 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11899 partConstrFrelid = partConstr->confrelid;
11900 partConstrRelid = partConstr->conrelid;
11901
11902 /*
11903 * If the referenced table is partitioned, then the partition we're
11904 * attaching now has extra pg_constraint rows and action triggers that are
11905 * no longer needed. Remove those.
11906 */
11908 {
11910
11913
11915 }
11916
11917 /*
11918 * Will we need to validate this constraint? A valid parent constraint
11919 * implies that all child constraints have been validated, so if this one
11920 * isn't, we must trigger phase 3 validation.
11921 */
11922 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11923
11926
11927 /*
11928 * The action triggers in the new partition become redundant -- the parent
11929 * table already has equivalent ones, and those will be able to reach the
11930 * partition. Remove the ones in the partition. We identify them because
11931 * they have our constraint OID, as well as being on the referenced rel.
11932 */
11935
11938
11939 /*
11940 * Like the constraint, attach partition's "check" triggers to the
11941 * corresponding parent triggers if the constraint is ENFORCED. NOT
11942 * ENFORCED constraints do not have these triggers.
11943 */
11945 {
11948
11958 }
11959
11960 /*
11961 * We updated this pg_constraint row above to set its parent; validating
11962 * it will cause its convalidated flag to change, so we need CCI here. In
11963 * addition, we need it unconditionally for the rare case where the parent
11964 * table has *two* identical constraints; when reaching this function for
11965 * the second one, we must have made our changes visible, otherwise we
11966 * would try to attach both to this one.
11967 */
11969
11970 /* If validation is needed, put it in the queue now. */
11971 if (queueValidation)
11972 {
11974 Oid confrelid;
11975
11977
11980 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11981
11982 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11983
11984 /* Use the same lock as for AT_ValidateConstraint */
11989 }
11990}
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:1222

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

20508{
20509 /*
20510 * Create an inheritance; the relevant checks are performed inside the
20511 * function.
20512 */
20513 CreateInheritance(attachrel, rel, true);
20514
20515 /* Update the pg_class entry. */
20516 StorePartitionBound(attachrel, rel, bound);
20517
20518 /* Ensure there exists a correct set of indexes in the partition. */
20520
20521 /* and triggers */
20523
20524 /*
20525 * Clone foreign key constraints. Callee is responsible for setting up
20526 * for phase 3 constraint verification.
20527 */
20529}
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition heap.c:4074
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 6981 of file tablecmds.c.

6983{
6984 ListCell *child;
6985 List *children;
6986
6987 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6988
6989 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6991 cmd->behavior);
6992
6993 foreach(child, children)
6994 {
6995 Oid childrelid = lfirst_oid(child);
6997
6998 childrel = relation_open(childrelid, lockmode);
7000 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
7002 }
7003}
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition tablecmds.c:7184

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

1430{
1431 int natts;
1433 ListCell *l;
1434 TupleDesc desc;
1435 char *attname;
1436 Oid atttypid;
1437 int32 atttypmod;
1438 Oid attcollation;
1439 int attdim;
1440
1441 /*
1442 * allocate a new tuple descriptor
1443 */
1444 natts = list_length(columns);
1445 desc = CreateTemplateTupleDesc(natts);
1446
1447 attnum = 0;
1448
1449 foreach(l, columns)
1450 {
1451 ColumnDef *entry = lfirst(l);
1454
1455 /*
1456 * for each entry in the list, get the name and type information from
1457 * the list and have TupleDescInitEntry fill in the attribute
1458 * information we need.
1459 */
1460 attnum++;
1461
1462 attname = entry->colname;
1463 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1464
1466 if (aclresult != ACLCHECK_OK)
1468
1469 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1471 if (attdim > PG_INT16_MAX)
1472 ereport(ERROR,
1474 errmsg("too many array dimensions"));
1475
1476 if (entry->typeName->setof)
1477 ereport(ERROR,
1479 errmsg("column \"%s\" cannot be declared SETOF",
1480 attname)));
1481
1483 atttypid, atttypmod, attdim);
1484 att = TupleDescAttr(desc, attnum - 1);
1485
1486 /* Override TupleDescInitEntry's settings as requested */
1487 TupleDescInitEntryCollation(desc, attnum, attcollation);
1488
1489 /* Fill in additional stuff not handled by TupleDescInitEntry */
1490 att->attnotnull = entry->is_not_null;
1491 att->attislocal = entry->is_local;
1492 att->attinhcount = entry->inhcount;
1493 att->attidentity = entry->identity;
1494 att->attgenerated = entry->generated;
1495 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1496 if (entry->storage)
1497 att->attstorage = entry->storage;
1498 else if (entry->storage_name)
1499 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1500
1502 }
1503
1504 TupleDescFinalize(desc);
1505
1506 return desc;
1507}
NameData attname
bool is_not_null
Definition parsenodes.h:775
char identity
Definition parsenodes.h:781
char * storage_name
Definition parsenodes.h:778
char * colname
Definition parsenodes.h:770
TypeName * typeName
Definition parsenodes.h:771
char generated
Definition parsenodes.h:784
char storage
Definition parsenodes.h:777
bool is_local
Definition parsenodes.h:774
int16 inhcount
Definition parsenodes.h:773
char * compression
Definition parsenodes.h:772
bool setof
Definition parsenodes.h:290
TupleDesc CreateTemplateTupleDesc(int natts)
Definition tupdesc.c:165
void TupleDescFinalize(TupleDesc tupdesc)
Definition tupdesc.c:511
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition tupdesc.c:100
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
Definition tupdesc.c:1093
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition tupdesc.c:909

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(), TupleDescFinalize(), 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 22396 of file tablecmds.c.

22397{
22398 /*
22399 * Build the needed expression execution states. Here, we expect only NOT
22400 * NULL and CHECK constraint.
22401 */
22403 {
22404 switch (con->contype)
22405 {
22406 case CONSTR_CHECK:
22407
22408 /*
22409 * We already expanded virtual expression in
22410 * createTableConstraints.
22411 */
22412 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22413 break;
22414 case CONSTR_NOTNULL:
22415 /* Nothing to do here. */
22416 break;
22417 default:
22418 elog(ERROR, "unrecognized constraint type: %d",
22419 (int) con->contype);
22420 }
22421 }
22422
22423 /* Expression already planned in createTableConstraints */
22425 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22426}

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

16571{
16573 SysScanDesc scan;
16574 ScanKeyData key[1];
16576
16578 ScanKeyInit(&key[0],
16581 ObjectIdGetDatum(relationOid));
16583 true, NULL, 1, key);
16585 {
16590 Acl *newAcl;
16592 bool isNull;
16593 HeapTuple newtuple;
16594
16595 /* Ignore dropped columns */
16596 if (att->attisdropped)
16597 continue;
16598
16602 &isNull);
16603 /* Null ACLs do not require changes */
16604 if (isNull)
16605 continue;
16606
16607 memset(repl_null, false, sizeof(repl_null));
16608 memset(repl_repl, false, sizeof(repl_repl));
16609
16614
16618
16619 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16620
16621 heap_freetuple(newtuple);
16622 }
16623 systable_endscan(scan);
16625}

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

16636{
16638 SysScanDesc scan;
16639 ScanKeyData key[2];
16640 HeapTuple tup;
16641
16642 /*
16643 * SERIAL sequences are those having an auto dependency on one of the
16644 * table's columns (we don't care *which* column, exactly).
16645 */
16647
16648 ScanKeyInit(&key[0],
16652 ScanKeyInit(&key[1],
16655 ObjectIdGetDatum(relationOid));
16656 /* we leave refobjsubid unspecified */
16657
16659 NULL, 2, key);
16660
16661 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16662 {
16665
16666 /* skip dependencies other than auto dependencies on columns */
16667 if (depForm->refobjsubid == 0 ||
16668 depForm->classid != RelationRelationId ||
16669 depForm->objsubid != 0 ||
16670 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16671 continue;
16672
16673 /* Use relation_open just in case it's an index */
16674 seqRel = relation_open(depForm->objid, lockmode);
16675
16676 /* skip non-sequence relations */
16677 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16678 {
16679 /* No need to keep the lock */
16680 relation_close(seqRel, lockmode);
16681 continue;
16682 }
16683
16684 /* We don't need to close the sequence while we alter it. */
16685 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16686
16687 /* Now we can close it. Keep the lock till end of transaction. */
16689 }
16690
16691 systable_endscan(scan);
16692
16694}

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

7738{
7740 int attnum;
7741
7742 /*
7743 * this test is deliberately not attisdropped-aware, since if one tries to
7744 * add a column matching a dropped column name, it's gonna fail anyway.
7745 */
7748 PointerGetDatum(colname));
7750 return true;
7751
7754
7755 /*
7756 * We throw a different error message for conflicts with system column
7757 * names, since they are normally not shown and the user might otherwise
7758 * be confused about the reason for the conflict.
7759 */
7760 if (attnum <= 0)
7761 ereport(ERROR,
7763 errmsg("column name \"%s\" conflicts with a system column name",
7764 colname)));
7765 else
7766 {
7767 if (if_not_exists)
7768 {
7771 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7772 colname, RelationGetRelationName(rel))));
7773 return false;
7774 }
7775
7776 ereport(ERROR,
7778 errmsg("column \"%s\" of relation \"%s\" already exists",
7779 colname, RelationGetRelationName(rel))));
7780 }
7781
7782 return true;
7783}
HeapTuple SearchSysCache2(SysCacheIdentifier cacheId, Datum key1, Datum key2)
Definition syscache.c:231

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

7234{
7236 bool typeOk = false;
7237
7238 if (typ->typtype == TYPTYPE_COMPOSITE)
7239 {
7241
7242 Assert(OidIsValid(typ->typrelid));
7244 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7245
7246 /*
7247 * Close the parent rel, but keep our AccessShareLock on it until xact
7248 * commit. That will prevent someone else from deleting or ALTERing
7249 * the type before the typed table creation/conversion commits.
7250 */
7252
7253 if (!typeOk)
7254 ereport(ERROR,
7256 errmsg("type %s is the row type of another table",
7257 format_type_be(typ->oid)),
7258 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7259 }
7260 else
7261 ereport(ERROR,
7263 errmsg("type %s is not a composite type",
7264 format_type_be(typ->oid))));
7265}

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

4507{
4508 /*
4509 * Don't allow ALTER on temp tables of other backends. Their local buffer
4510 * manager is not going to cope if we need to change the table's contents.
4511 * Even if we don't, there may be optimizations that assume temp tables
4512 * aren't subject to such interference.
4513 */
4514 if (RELATION_IS_OTHER_TEMP(rel))
4515 ereport(ERROR,
4517 errmsg("cannot alter temporary tables of other sessions")));
4518
4519 /*
4520 * Also check for active uses of the relation in the current transaction,
4521 * including open scans and pending AFTER trigger events.
4522 */
4523 CheckTableNotInUse(rel, "ALTER TABLE");
4524}
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition tablecmds.c:4473

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

13922{
13923 Oid roleid = GetUserId();
13925 int i;
13926
13927 /* Okay if we have relation-level REFERENCES permission */
13930 if (aclresult == ACLCHECK_OK)
13931 return;
13932 /* Else we must have REFERENCES on each column */
13933 for (i = 0; i < natts; i++)
13934 {
13936 roleid, ACL_REFERENCES);
13937 if (aclresult != ACLCHECK_OK)
13940 }
13941}
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition aclchk.c:3912
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4083
#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 3750 of file tablecmds.c.

3751{
3753
3754 /*
3755 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3756 * stored as 0.
3757 */
3758 oldTableSpaceId = rel->rd_rel->reltablespace;
3761 return false;
3762
3763 /*
3764 * We cannot support moving mapped relations into different tablespaces.
3765 * (In particular this eliminates all shared catalogs.)
3766 */
3767 if (RelationIsMapped(rel))
3768 ereport(ERROR,
3770 errmsg("cannot move system relation \"%s\"",
3772
3773 /* Cannot move a non-shared relation into pg_global */
3775 ereport(ERROR,
3777 errmsg("only shared relations can be placed in pg_global tablespace")));
3778
3779 /*
3780 * Do not allow moving temp tables of other backends ... their local
3781 * buffer manager is not going to cope.
3782 */
3783 if (RELATION_IS_OTHER_TEMP(rel))
3784 ereport(ERROR,
3786 errmsg("cannot move temporary tables of other sessions")));
3787
3788 return true;
3789}
#define RelationIsMapped(relation)
Definition rel.h:565

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

4474{
4475 int expected_refcnt;
4476
4477 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4478 if (rel->rd_refcnt != expected_refcnt)
4479 ereport(ERROR,
4481 /* translator: first %s is a SQL command, eg ALTER TABLE */
4482 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4484
4485 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4486 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4488 ereport(ERROR,
4490 /* translator: first %s is a SQL command, eg ALTER TABLE */
4491 errmsg("cannot %s \"%s\" because it has pending trigger events",
4493}
int rd_refcnt
Definition rel.h:59
bool rd_isnailed
Definition rel.h:62
bool AfterTriggerPendingOnRel(Oid relid)
Definition trigger.c:6150

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

9949{
9950 char buf[NAMEDATALEN * 2];
9951 int buflen = 0;
9952 ListCell *lc;
9953
9954 buf[0] = '\0';
9955 foreach(lc, colnames)
9956 {
9957 const char *name = strVal(lfirst(lc));
9958
9959 if (buflen > 0)
9960 buf[buflen++] = '_'; /* insert _ between names */
9961
9962 /*
9963 * At this point we have buflen <= NAMEDATALEN. name should be less
9964 * than NAMEDATALEN already, but use strlcpy for paranoia.
9965 */
9966 strlcpy(buf + buflen, name, NAMEDATALEN);
9967 buflen += strlen(buf + buflen);
9968 if (buflen >= NAMEDATALEN)
9969 break;
9970 }
9971 return pstrdup(buf);
9972}
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 11324 of file tablecmds.c.

11325{
11327 AttrMap *attmap;
11328 ListCell *cell;
11329 SysScanDesc scan;
11330 ScanKeyData key[2];
11331 HeapTuple tuple;
11332 List *clone = NIL;
11334
11335 /*
11336 * Search for any constraints where this partition's parent is in the
11337 * referenced side. However, we must not clone any constraint whose
11338 * parent constraint is also going to be cloned, to avoid duplicates. So
11339 * do it in two steps: first construct the list of constraints to clone,
11340 * then go over that list cloning those whose parents are not in the list.
11341 * (We must not rely on the parent being seen first, since the catalog
11342 * scan could return children first.)
11343 */
11345 ScanKeyInit(&key[0],
11348 ScanKeyInit(&key[1],
11351 /* This is a seqscan, as we don't have a usable index ... */
11353 NULL, 2, key);
11354 while ((tuple = systable_getnext(scan)) != NULL)
11355 {
11357
11359 }
11360 systable_endscan(scan);
11362
11363 /*
11364 * Triggers of the foreign keys will be manipulated a bunch of times in
11365 * the loop below. To avoid repeatedly opening/closing the trigger
11366 * catalog relation, we open it here and pass it to the subroutines called
11367 * below.
11368 */
11370
11373 false);
11374 foreach(cell, clone)
11375 {
11376 Oid constrOid = lfirst_oid(cell);
11379 Oid indexOid;
11381 int numfks;
11388 int numfkdelsetcols;
11389 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11391 ObjectAddress address;
11394
11396 if (!HeapTupleIsValid(tuple))
11397 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11399
11400 /*
11401 * As explained above: don't try to clone a constraint for which we're
11402 * going to clone the parent.
11403 */
11404 if (list_member_oid(clone, constrForm->conparentid))
11405 {
11406 ReleaseSysCache(tuple);
11407 continue;
11408 }
11409
11410 /* We need the same lock level that CreateTrigger will acquire */
11412
11413 indexOid = constrForm->conindid;
11415 &numfks,
11416 conkey,
11417 confkey,
11418 conpfeqop,
11419 conppeqop,
11420 conffeqop,
11422 confdelsetcols);
11423
11424 for (int i = 0; i < numfks; i++)
11425 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11426
11429 fkconstraint->conname = NameStr(constrForm->conname);
11430 fkconstraint->deferrable = constrForm->condeferrable;
11431 fkconstraint->initdeferred = constrForm->condeferred;
11432 fkconstraint->location = -1;
11433 fkconstraint->pktable = NULL;
11434 /* ->fk_attrs determined below */
11435 fkconstraint->pk_attrs = NIL;
11436 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11437 fkconstraint->fk_upd_action = constrForm->confupdtype;
11438 fkconstraint->fk_del_action = constrForm->confdeltype;
11439 fkconstraint->fk_del_set_cols = NIL;
11440 fkconstraint->old_conpfeqop = NIL;
11441 fkconstraint->old_pktable_oid = InvalidOid;
11442 fkconstraint->is_enforced = constrForm->conenforced;
11443 fkconstraint->skip_validation = false;
11444 fkconstraint->initially_valid = constrForm->convalidated;
11445
11446 /* set up colnames that are used to generate the constraint name */
11447 for (int i = 0; i < numfks; i++)
11448 {
11450
11452 conkey[i] - 1);
11453 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11454 makeString(NameStr(att->attname)));
11455 }
11456
11457 /*
11458 * Add the new foreign key constraint pointing to the new partition.
11459 * Because this new partition appears in the referenced side of the
11460 * constraint, we don't need to set up for Phase 3 check.
11461 */
11463 if (!OidIsValid(partIndexId))
11464 elog(ERROR, "index for %u not found in partition %s",
11466
11467 /*
11468 * Get the "action" triggers belonging to the constraint to pass as
11469 * parent OIDs for similar triggers that will be created on the
11470 * partition in addFkRecurseReferenced().
11471 */
11472 if (constrForm->conenforced)
11474 constrForm->confrelid, constrForm->conrelid,
11476
11477 /* Add this constraint ... */
11479 fkconstraint->conname, fkconstraint, fkRel,
11483 numfkdelsetcols, confdelsetcols, false,
11484 constrForm->conperiod);
11485 /* ... and recurse */
11487 fkRel,
11490 address.objectId,
11491 numfks,
11493 conkey,
11494 conpfeqop,
11495 conppeqop,
11496 conffeqop,
11498 confdelsetcols,
11499 true,
11502 constrForm->conperiod);
11503
11505 ReleaseSysCache(tuple);
11506 }
11507
11509}
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 11525 of file tablecmds.c.

11526{
11527 AttrMap *attmap;
11528 List *partFKs;
11529 List *clone = NIL;
11530 ListCell *cell;
11532
11533 /* obtain a list of constraints that we need to clone */
11534 foreach(cell, RelationGetFKeyList(parentRel))
11535 {
11536 ForeignKeyCacheInfo *fk = lfirst(cell);
11537
11538 /*
11539 * Refuse to attach a table as partition that this partitioned table
11540 * already has a foreign key to. This isn't useful schema, which is
11541 * proven by the fact that there have been no user complaints that
11542 * it's already impossible to achieve this in the opposite direction,
11543 * i.e., creating a foreign key that references a partition. This
11544 * restriction allows us to dodge some complexities around
11545 * pg_constraint and pg_trigger row creations that would be needed
11546 * during ATTACH/DETACH for this kind of relationship.
11547 */
11548 if (fk->confrelid == RelationGetRelid(partRel))
11549 ereport(ERROR,
11551 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11552 RelationGetRelationName(partRel),
11553 get_constraint_name(fk->conoid))));
11554
11555 clone = lappend_oid(clone, fk->conoid);
11556 }
11557
11558 /*
11559 * Silently do nothing if there's nothing to do. In particular, this
11560 * avoids throwing a spurious error for foreign tables.
11561 */
11562 if (clone == NIL)
11563 return;
11564
11565 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11566 ereport(ERROR,
11568 errmsg("foreign key constraints are not supported on foreign tables")));
11569
11570 /*
11571 * Triggers of the foreign keys will be manipulated a bunch of times in
11572 * the loop below. To avoid repeatedly opening/closing the trigger
11573 * catalog relation, we open it here and pass it to the subroutines called
11574 * below.
11575 */
11577
11578 /*
11579 * The constraint key may differ, if the columns in the partition are
11580 * different. This map is used to convert them.
11581 */
11584 false);
11585
11587
11588 foreach(cell, clone)
11589 {
11593 HeapTuple tuple;
11594 int numfks;
11601 int numfkdelsetcols;
11602 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11604 bool attached;
11605 Oid indexOid;
11606 ObjectAddress address;
11607 ListCell *lc;
11610 bool with_period;
11611
11613 if (!HeapTupleIsValid(tuple))
11614 elog(ERROR, "cache lookup failed for constraint %u",
11617
11618 /* Don't clone constraints whose parents are being cloned */
11619 if (list_member_oid(clone, constrForm->conparentid))
11620 {
11621 ReleaseSysCache(tuple);
11622 continue;
11623 }
11624
11625 /*
11626 * Need to prevent concurrent deletions. If pkrel is a partitioned
11627 * relation, that means to lock all partitions.
11628 */
11630 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11633
11636 &numfkdelsetcols, confdelsetcols);
11637 for (int i = 0; i < numfks; i++)
11638 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11639
11640 /*
11641 * Get the "check" triggers belonging to the constraint, if it is
11642 * ENFORCED, to pass as parent OIDs for similar triggers that will be
11643 * created on the partition in addFkRecurseReferencing(). They are
11644 * also passed to tryAttachPartitionForeignKey() below to simply
11645 * assign as parents to the partition's existing "check" triggers,
11646 * that is, if the corresponding constraints is deemed attachable to
11647 * the parent constraint.
11648 */
11649 if (constrForm->conenforced)
11651 constrForm->confrelid, constrForm->conrelid,
11653
11654 /*
11655 * Before creating a new constraint, see whether any existing FKs are
11656 * fit for the purpose. If one is, attach the parent constraint to
11657 * it, and don't clone anything. This way we avoid the expensive
11658 * verification step and don't end up with a duplicate FK, and we
11659 * don't need to recurse to partitions for this constraint.
11660 */
11661 attached = false;
11662 foreach(lc, partFKs)
11663 {
11665
11667 fk,
11668 partRel,
11670 numfks,
11672 confkey,
11673 conpfeqop,
11676 trigrel))
11677 {
11678 attached = true;
11680 break;
11681 }
11682 }
11683 if (attached)
11684 {
11685 ReleaseSysCache(tuple);
11686 continue;
11687 }
11688
11689 /* No dice. Set up to create our own constraint */
11692 /* ->conname determined below */
11693 fkconstraint->deferrable = constrForm->condeferrable;
11694 fkconstraint->initdeferred = constrForm->condeferred;
11695 fkconstraint->location = -1;
11696 fkconstraint->pktable = NULL;
11697 /* ->fk_attrs determined below */
11698 fkconstraint->pk_attrs = NIL;
11699 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11700 fkconstraint->fk_upd_action = constrForm->confupdtype;
11701 fkconstraint->fk_del_action = constrForm->confdeltype;
11702 fkconstraint->fk_del_set_cols = NIL;
11703 fkconstraint->old_conpfeqop = NIL;
11704 fkconstraint->old_pktable_oid = InvalidOid;
11705 fkconstraint->is_enforced = constrForm->conenforced;
11706 fkconstraint->skip_validation = false;
11707 fkconstraint->initially_valid = constrForm->convalidated;
11708 for (int i = 0; i < numfks; i++)
11709 {
11711
11712 att = TupleDescAttr(RelationGetDescr(partRel),
11713 mapped_conkey[i] - 1);
11714 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11715 makeString(NameStr(att->attname)));
11716 }
11717
11718 indexOid = constrForm->conindid;
11719 with_period = constrForm->conperiod;
11720
11721 /* Create the pg_constraint entry at this level */
11723 NameStr(constrForm->conname), fkconstraint,
11724 partRel, pkrel, indexOid, parentConstrOid,
11725 numfks, confkey,
11728 numfkdelsetcols, confdelsetcols,
11729 false, with_period);
11730
11731 /* Done with the cloned constraint's tuple */
11732 ReleaseSysCache(tuple);
11733
11734 /* Create the check triggers, and recurse to partitions, if any */
11737 partRel,
11738 pkrel,
11739 indexOid,
11740 address.objectId,
11741 numfks,
11742 confkey,
11744 conpfeqop,
11745 conppeqop,
11746 conffeqop,
11748 confdelsetcols,
11749 false, /* no old check exists */
11753 with_period);
11755 }
11756
11758}

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

11297{
11298 /* This only works for declarative partitioning */
11299 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11300
11301 /*
11302 * First, clone constraints where the parent is on the referencing side.
11303 */
11305
11306 /*
11307 * Clone constraints for which the parent is on the referenced side.
11308 */
11310}
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 21070 of file tablecmds.c.

21071{
21074 SysScanDesc scan;
21075 HeapTuple tuple;
21077
21082 true, NULL, 1, &key);
21083
21085 "clone trig", ALLOCSET_SMALL_SIZES);
21086
21087 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
21088 {
21091 Node *qual = NULL;
21092 Datum value;
21093 bool isnull;
21094 List *cols = NIL;
21095 List *trigargs = NIL;
21097
21098 /*
21099 * Ignore statement-level triggers; those are not cloned.
21100 */
21101 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
21102 continue;
21103
21104 /*
21105 * Don't clone internal triggers, because the constraint cloning code
21106 * will.
21107 */
21108 if (trigForm->tgisinternal)
21109 continue;
21110
21111 /*
21112 * Complain if we find an unexpected trigger type.
21113 */
21114 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
21115 !TRIGGER_FOR_AFTER(trigForm->tgtype))
21116 elog(ERROR, "unexpected trigger \"%s\" found",
21117 NameStr(trigForm->tgname));
21118
21119 /* Use short-lived context for CREATE TRIGGER */
21121
21122 /*
21123 * If there is a WHEN clause, generate a 'cooked' version of it that's
21124 * appropriate for the partition.
21125 */
21127 RelationGetDescr(pg_trigger), &isnull);
21128 if (!isnull)
21129 {
21131 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
21132 partition, parent);
21133 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
21134 partition, parent);
21135 }
21136
21137 /*
21138 * If there is a column list, transform it to a list of column names.
21139 * Note we don't need to map this list in any way ...
21140 */
21141 if (trigForm->tgattr.dim1 > 0)
21142 {
21143 int i;
21144
21145 for (i = 0; i < trigForm->tgattr.dim1; i++)
21146 {
21148
21149 col = TupleDescAttr(parent->rd_att,
21150 trigForm->tgattr.values[i] - 1);
21151 cols = lappend(cols,
21152 makeString(pstrdup(NameStr(col->attname))));
21153 }
21154 }
21155
21156 /* Reconstruct trigger arguments list. */
21157 if (trigForm->tgnargs > 0)
21158 {
21159 char *p;
21160
21162 RelationGetDescr(pg_trigger), &isnull);
21163 if (isnull)
21164 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
21166
21167 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
21168
21169 for (int i = 0; i < trigForm->tgnargs; i++)
21170 {
21172 p += strlen(p) + 1;
21173 }
21174 }
21175
21177 trigStmt->replace = false;
21178 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
21179 trigStmt->trigname = NameStr(trigForm->tgname);
21180 trigStmt->relation = NULL;
21181 trigStmt->funcname = NULL; /* passed separately */
21182 trigStmt->args = trigargs;
21183 trigStmt->row = true;
21184 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
21185 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
21186 trigStmt->columns = cols;
21187 trigStmt->whenClause = NULL; /* passed separately */
21188 trigStmt->transitionRels = NIL; /* not supported at present */
21189 trigStmt->deferrable = trigForm->tgdeferrable;
21190 trigStmt->initdeferred = trigForm->tginitdeferred;
21191 trigStmt->constrrel = NULL; /* passed separately */
21192
21194 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
21195 trigForm->tgfoid, trigForm->oid, qual,
21196 false, true, trigForm->tgenabled);
21197
21200 }
21201
21203
21204 systable_endscan(scan);
21206}
#define DatumGetByteaPP(X)
Definition fmgr.h:292
static struct @177 value
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
#define ALLOCSET_SMALL_SIZES
Definition memutils.h:170
#define PRS2_OLD_VARNO
Definition primnodes.h:251
#define PRS2_NEW_VARNO
Definition primnodes.h:252
ObjectAddress CreateTriggerFiringOn(const 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:179
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().

◆ cmp_partition_index_ext_dep()

static int cmp_partition_index_ext_dep ( const ListCell a,
const ListCell b 
)
static

Definition at line 23051 of file tablecmds.c.

23052{
23055
23056 if (ea->parentIndexOid != eb->parentIndexOid)
23057 return pg_cmp_u32(ea->parentIndexOid, eb->parentIndexOid);
23058 return pg_cmp_u32(ea->indexOid, eb->indexOid);
23059}
static int pg_cmp_u32(uint32 a, uint32 b)
Definition int.h:719
int b
Definition isn.c:74
int a
Definition isn.c:73

References a, b, fb(), lfirst, and pg_cmp_u32().

Referenced by collectPartitionIndexExtDeps().

◆ collectPartitionIndexExtDeps()

static List * collectPartitionIndexExtDeps ( List partitionOids)
static

Definition at line 23079 of file tablecmds.c.

23080{
23081 List *collected = NIL;
23082 List *result = NIL;
23084
23085 /*
23086 * Phase 1: collect one entry per (partition index -> parent index) pair,
23087 * with its extension dependency OIDs sorted ascending.
23088 */
23090 {
23091 Relation partRel;
23092 List *indexList;
23093
23094 /*
23095 * Use NoLock since the caller already holds AccessExclusiveLock on
23096 * these partitions.
23097 */
23098 partRel = table_open(partOid, NoLock);
23100
23101 foreach_oid(indexOid, indexList)
23102 {
23103 Oid parentIndexOid;
23105
23106 if (!get_rel_relispartition(indexOid))
23107 continue;
23108
23109 parentIndexOid = get_partition_parent(indexOid, true);
23110 if (!OidIsValid(parentIndexOid))
23111 continue;
23112
23113 entry = palloc(sizeof(PartitionIndexExtDepEntry));
23114 entry->parentIndexOid = parentIndexOid;
23115 entry->indexOid = indexOid;
23117 indexOid);
23119
23120 collected = lappend(collected, entry);
23121 }
23122
23124 table_close(partRel, NoLock);
23125 }
23126
23127 /*
23128 * Phase 2: sort by parentIndexOid so entries sharing a parent index sit
23129 * adjacent.
23130 */
23132
23133 /*
23134 * Phase 3: single linear pass verifying that adjacent entries sharing a
23135 * parent index have identical extension dependencies, and keeping one
23136 * representative entry per parent index.
23137 */
23139 {
23140 if (prev != NULL && prev->parentIndexOid == entry->parentIndexOid)
23141 {
23142 if (!equal_oid_lists(prev->extensionOids, entry->extensionOids))
23143 ereport(ERROR,
23145 errmsg("cannot merge partitions with conflicting extension dependencies"),
23146 errdetail("Partition indexes \"%s\" and \"%s\" depend on different extensions.",
23147 get_rel_name(prev->indexOid),
23148 get_rel_name(entry->indexOid))));
23149
23150 /* Duplicate entry for the same parent index; discard. */
23151 list_free(entry->extensionOids);
23152 pfree(entry);
23153 continue;
23154 }
23155
23156 result = lappend(result, entry);
23157 prev = entry;
23158 }
23159
23161
23162 return result;
23163}
uint32 result
void list_sort(List *list, list_sort_comparator cmp)
Definition list.c:1674
int list_oid_cmp(const ListCell *p1, const ListCell *p2)
Definition list.c:1703
void * palloc(Size size)
Definition mcxt.c:1390
List * getAutoExtensionsOfObject(Oid classId, Oid objectId)
Definition pg_depend.c:911
static bool equal_oid_lists(const List *a, const List *b)
static int cmp_partition_index_ext_dep(const ListCell *a, const ListCell *b)

References cmp_partition_index_ext_dep(), equal_oid_lists(), ereport, errcode(), errdetail(), errmsg, ERROR, PartitionIndexExtDepEntry::extensionOids, fb(), foreach_oid, foreach_ptr, get_partition_parent(), get_rel_name(), get_rel_relispartition(), getAutoExtensionsOfObject(), PartitionIndexExtDepEntry::indexOid, lappend(), list_free(), list_oid_cmp(), list_sort(), NIL, NoLock, OidIsValid, palloc(), PartitionIndexExtDepEntry::parentIndexOid, pfree(), RelationGetIndexList(), result, table_close(), and table_open().

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ ComputePartitionAttrs()

static void ComputePartitionAttrs ( ParseState pstate,
Relation  rel,
List partParams,
AttrNumber partattrs,
List **  partexprs,
Oid partopclass,
Oid partcollation,
PartitionStrategy  strategy 
)
static

Definition at line 20038 of file tablecmds.c.

20041{
20042 int attn;
20043 ListCell *lc;
20044 Oid am_oid;
20045
20046 attn = 0;
20047 foreach(lc, partParams)
20048 {
20050 Oid atttype;
20051 Oid attcollation;
20052
20053 if (pelem->name != NULL)
20054 {
20055 /* Simple attribute reference */
20058
20060 pelem->name);
20062 ereport(ERROR,
20064 errmsg("column \"%s\" named in partition key does not exist",
20065 pelem->name),
20066 parser_errposition(pstate, pelem->location)));
20068
20069 if (attform->attnum <= 0)
20070 ereport(ERROR,
20072 errmsg("cannot use system column \"%s\" in partition key",
20073 pelem->name),
20074 parser_errposition(pstate, pelem->location)));
20075
20076 /*
20077 * Stored generated columns cannot work: They are computed after
20078 * BEFORE triggers, but partition routing is done before all
20079 * triggers. Maybe virtual generated columns could be made to
20080 * work, but then they would need to be handled as an expression
20081 * below.
20082 */
20083 if (attform->attgenerated)
20084 ereport(ERROR,
20086 errmsg("cannot use generated column in partition key"),
20087 errdetail("Column \"%s\" is a generated column.",
20088 pelem->name),
20089 parser_errposition(pstate, pelem->location)));
20090
20091 partattrs[attn] = attform->attnum;
20092 atttype = attform->atttypid;
20093 attcollation = attform->attcollation;
20095 }
20096 else
20097 {
20098 /* Expression */
20099 Node *expr = pelem->expr;
20100 char partattname[16];
20102 int i;
20103
20104 Assert(expr != NULL);
20105 atttype = exprType(expr);
20106 attcollation = exprCollation(expr);
20107
20108 /*
20109 * The expression must be of a storable type (e.g., not RECORD).
20110 * The test is the same as for whether a table column is of a safe
20111 * type (which is why we needn't check for the non-expression
20112 * case).
20113 */
20114 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
20116 atttype, attcollation,
20118
20119 /*
20120 * Strip any top-level COLLATE clause. This ensures that we treat
20121 * "x COLLATE y" and "(x COLLATE y)" alike.
20122 */
20123 while (IsA(expr, CollateExpr))
20124 expr = (Node *) ((CollateExpr *) expr)->arg;
20125
20126 /*
20127 * Examine all the columns in the partition key expression. When
20128 * the whole-row reference is present, examine all the columns of
20129 * the partitioned table.
20130 */
20131 pull_varattnos(expr, 1, &expr_attrs);
20133 {
20138 }
20139
20140 i = -1;
20141 while ((i = bms_next_member(expr_attrs, i)) >= 0)
20142 {
20144
20145 Assert(attno != 0);
20146
20147 /*
20148 * Cannot allow system column references, since that would
20149 * make partition routing impossible: their values won't be
20150 * known yet when we need to do that.
20151 */
20152 if (attno < 0)
20153 ereport(ERROR,
20155 errmsg("partition key expressions cannot contain system column references")));
20156
20157 /*
20158 * Stored generated columns cannot work: They are computed
20159 * after BEFORE triggers, but partition routing is done before
20160 * all triggers. Virtual generated columns could probably
20161 * work, but it would require more work elsewhere (for example
20162 * SET EXPRESSION would need to check whether the column is
20163 * used in partition keys). Seems safer to prohibit for now.
20164 */
20165 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
20166 ereport(ERROR,
20168 errmsg("cannot use generated column in partition key"),
20169 errdetail("Column \"%s\" is a generated column.",
20170 get_attname(RelationGetRelid(rel), attno, false)),
20171 parser_errposition(pstate, pelem->location)));
20172 }
20173
20174 if (IsA(expr, Var) &&
20175 ((Var *) expr)->varattno > 0)
20176 {
20177
20178 /*
20179 * User wrote "(column)" or "(column COLLATE something)".
20180 * Treat it like simple attribute anyway.
20181 */
20182 partattrs[attn] = ((Var *) expr)->varattno;
20183 }
20184 else
20185 {
20186 partattrs[attn] = 0; /* marks the column as expression */
20187 *partexprs = lappend(*partexprs, expr);
20188
20189 /*
20190 * transformPartitionSpec() should have already rejected
20191 * subqueries, aggregates, window functions, and SRFs, based
20192 * on the EXPR_KIND_ for partition expressions.
20193 */
20194
20195 /*
20196 * Preprocess the expression before checking for mutability.
20197 * This is essential for the reasons described in
20198 * contain_mutable_functions_after_planning. However, we call
20199 * expression_planner for ourselves rather than using that
20200 * function, because if constant-folding reduces the
20201 * expression to a constant, we'd like to know that so we can
20202 * complain below.
20203 *
20204 * Like contain_mutable_functions_after_planning, assume that
20205 * expression_planner won't scribble on its input, so this
20206 * won't affect the partexprs entry we saved above.
20207 */
20208 expr = (Node *) expression_planner((Expr *) expr);
20209
20210 /*
20211 * Partition expressions cannot contain mutable functions,
20212 * because a given row must always map to the same partition
20213 * as long as there is no change in the partition boundary
20214 * structure.
20215 */
20216 if (contain_mutable_functions(expr))
20217 ereport(ERROR,
20219 errmsg("functions in partition key expression must be marked IMMUTABLE")));
20220
20221 /*
20222 * While it is not exactly *wrong* for a partition expression
20223 * to be a constant, it seems better to reject such keys.
20224 */
20225 if (IsA(expr, Const))
20226 ereport(ERROR,
20228 errmsg("cannot use constant expression as partition key")));
20229 }
20230 }
20231
20232 /*
20233 * Apply collation override if any
20234 */
20235 if (pelem->collation)
20236 attcollation = get_collation_oid(pelem->collation, false);
20237
20238 /*
20239 * Check we have a collation iff it's a collatable type. The only
20240 * expected failures here are (1) COLLATE applied to a noncollatable
20241 * type, or (2) partition expression had an unresolved collation. But
20242 * we might as well code this to be a complete consistency check.
20243 */
20244 if (type_is_collatable(atttype))
20245 {
20246 if (!OidIsValid(attcollation))
20247 ereport(ERROR,
20249 errmsg("could not determine which collation to use for partition expression"),
20250 errhint("Use the COLLATE clause to set the collation explicitly.")));
20251 }
20252 else
20253 {
20254 if (OidIsValid(attcollation))
20255 ereport(ERROR,
20257 errmsg("collations are not supported by type %s",
20258 format_type_be(atttype))));
20259 }
20260
20261 partcollation[attn] = attcollation;
20262
20263 /*
20264 * Identify the appropriate operator class. For list and range
20265 * partitioning, we use a btree operator class; hash partitioning uses
20266 * a hash operator class.
20267 */
20268 if (strategy == PARTITION_STRATEGY_HASH)
20269 am_oid = HASH_AM_OID;
20270 else
20271 am_oid = BTREE_AM_OID;
20272
20273 if (!pelem->opclass)
20274 {
20275 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
20276
20278 {
20279 if (strategy == PARTITION_STRATEGY_HASH)
20280 ereport(ERROR,
20282 errmsg("data type %s has no default operator class for access method \"%s\"",
20283 format_type_be(atttype), "hash"),
20284 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
20285 else
20286 ereport(ERROR,
20288 errmsg("data type %s has no default operator class for access method \"%s\"",
20289 format_type_be(atttype), "btree"),
20290 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
20291 }
20292 }
20293 else
20295 atttype,
20296 am_oid == HASH_AM_OID ? "hash" : "btree",
20297 am_oid);
20298
20299 attn++;
20300 }
20301}
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:383
#define CHKATYPE_IS_PARTKEY
Definition heap.h:25
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition indexcmds.c:2371
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition indexcmds.c:2286
bool type_is_collatable(Oid typid)
Definition lsyscache.c:3389
Oid get_collation_oid(List *collname, bool missing_ok)
Definition namespace.c:4043
Oid exprCollation(const Node *expr)
Definition nodeFuncs.c:826
@ PARTITION_STRATEGY_HASH
Definition parsenodes.h:919
#define snprintf
Definition port.h:261
#define RelationGetNumberOfAttributes(relation)
Definition rel.h:522
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 20367 of file tablecmds.c.

20368{
20370 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20371 int num_check,
20372 i;
20373
20374 num_check = (constr != NULL) ? constr->num_check : 0;
20375 for (i = 0; i < num_check; i++)
20376 {
20377 Node *cexpr;
20378
20379 /*
20380 * If this constraint hasn't been fully validated yet, we must ignore
20381 * it here.
20382 */
20383 if (!constr->check[i].ccvalid)
20384 continue;
20385
20386 /*
20387 * NOT ENFORCED constraints are always marked as invalid, which should
20388 * have been ignored.
20389 */
20390 Assert(constr->check[i].ccenforced);
20391
20392 cexpr = stringToNode(constr->check[i].ccbin);
20393
20394 /*
20395 * Run each expression through const-simplification and
20396 * canonicalization. It is necessary, because we will be comparing it
20397 * to similarly-processed partition constraint expressions, and may
20398 * fail to detect valid matches without this.
20399 */
20400 cexpr = eval_const_expressions(NULL, cexpr);
20401 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20402
20404 make_ands_implicit((Expr *) cexpr));
20405 }
20406
20407 /*
20408 * Try to make the proof. Since we are comparing CHECK constraints, we
20409 * need to use weak implication, i.e., we assume existConstraint is
20410 * not-false and try to prove the same for testConstraint.
20411 *
20412 * Note that predicate_implied_by assumes its first argument is known
20413 * immutable. That should always be true for both NOT NULL and partition
20414 * constraints, so we don't test it here.
20415 */
20417}
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:154
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 17724 of file tablecmds.c.

17725{
17728
17729 if (acon->condeferrable != bcon->condeferrable ||
17730 acon->condeferred != bcon->condeferred ||
17733 return false;
17734 else
17735 return true;
17736}
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 14050 of file tablecmds.c.

14053{
14056
14057 /*
14058 * Note: for a self-referential FK (referencing and referenced tables are
14059 * the same), it is important that the ON UPDATE action fires before the
14060 * CHECK action, since both triggers will fire on the same row during an
14061 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
14062 * state of the row. Triggers fire in name order, so we ensure this by
14063 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
14064 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
14065 */
14067 fk_trigger->replace = false;
14068 fk_trigger->isconstraint = true;
14069 fk_trigger->trigname = "RI_ConstraintTrigger_c";
14070 fk_trigger->relation = NULL;
14071
14072 /* Either ON INSERT or ON UPDATE */
14073 if (on_insert)
14074 {
14075 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
14077 }
14078 else
14079 {
14080 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
14082 }
14083
14084 fk_trigger->args = NIL;
14085 fk_trigger->row = true;
14087 fk_trigger->columns = NIL;
14088 fk_trigger->whenClause = NULL;
14089 fk_trigger->transitionRels = NIL;
14090 fk_trigger->deferrable = fkconstraint->deferrable;
14091 fk_trigger->initdeferred = fkconstraint->initdeferred;
14092 fk_trigger->constrrel = NULL;
14093
14095 constraintOid, indexOid, InvalidOid,
14096 parentTrigOid, NULL, true, false);
14097
14098 /* Make changes-so-far visible */
14100
14101 return trigAddress.objectId;
14102}
List * SystemFuncName(char *name)
ObjectAddress CreateTrigger(const 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:162

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

14117{
14120
14121 /*
14122 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14123 * DELETE action on the referenced table.
14124 */
14126 fk_trigger->replace = false;
14127 fk_trigger->isconstraint = true;
14128 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14129 fk_trigger->relation = NULL;
14130 fk_trigger->args = NIL;
14131 fk_trigger->row = true;
14134 fk_trigger->columns = NIL;
14135 fk_trigger->whenClause = NULL;
14136 fk_trigger->transitionRels = NIL;
14137 fk_trigger->constrrel = NULL;
14138
14139 switch (fkconstraint->fk_del_action)
14140 {
14142 fk_trigger->deferrable = fkconstraint->deferrable;
14143 fk_trigger->initdeferred = fkconstraint->initdeferred;
14144 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
14145 break;
14147 fk_trigger->deferrable = false;
14148 fk_trigger->initdeferred = false;
14149 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
14150 break;
14152 fk_trigger->deferrable = false;
14153 fk_trigger->initdeferred = false;
14154 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
14155 break;
14157 fk_trigger->deferrable = false;
14158 fk_trigger->initdeferred = false;
14159 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
14160 break;
14162 fk_trigger->deferrable = false;
14163 fk_trigger->initdeferred = false;
14164 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
14165 break;
14166 default:
14167 elog(ERROR, "unrecognized FK action type: %d",
14168 (int) fkconstraint->fk_del_action);
14169 break;
14170 }
14171
14173 constraintOid, indexOid, InvalidOid,
14174 parentDelTrigger, NULL, true, false);
14175 if (deleteTrigOid)
14176 *deleteTrigOid = trigAddress.objectId;
14177
14178 /* Make changes-so-far visible */
14180
14181 /*
14182 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14183 * UPDATE action on the referenced table.
14184 */
14186 fk_trigger->replace = false;
14187 fk_trigger->isconstraint = true;
14188 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14189 fk_trigger->relation = NULL;
14190 fk_trigger->args = NIL;
14191 fk_trigger->row = true;
14194 fk_trigger->columns = NIL;
14195 fk_trigger->whenClause = NULL;
14196 fk_trigger->transitionRels = NIL;
14197 fk_trigger->constrrel = NULL;
14198
14199 switch (fkconstraint->fk_upd_action)
14200 {
14202 fk_trigger->deferrable = fkconstraint->deferrable;
14203 fk_trigger->initdeferred = fkconstraint->initdeferred;
14204 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
14205 break;
14207 fk_trigger->deferrable = false;
14208 fk_trigger->initdeferred = false;
14209 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
14210 break;
14212 fk_trigger->deferrable = false;
14213 fk_trigger->initdeferred = false;
14214 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
14215 break;
14217 fk_trigger->deferrable = false;
14218 fk_trigger->initdeferred = false;
14219 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
14220 break;
14222 fk_trigger->deferrable = false;
14223 fk_trigger->initdeferred = false;
14224 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
14225 break;
14226 default:
14227 elog(ERROR, "unrecognized FK action type: %d",
14228 (int) fkconstraint->fk_upd_action);
14229 break;
14230 }
14231
14233 constraintOid, indexOid, InvalidOid,
14234 parentUpdTrigger, NULL, true, false);
14235 if (updateTrigOid)
14236 *updateTrigOid = trigAddress.objectId;
14237}
#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 ATExecAlterFKConstrEnforceability().

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

14253{
14255 constraintOid, indexOid,
14256 parentInsTrigger, true);
14258 constraintOid, indexOid,
14259 parentUpdTrigger, false);
14260}
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 ATExecAlterFKConstrEnforceability().

◆ CreateInheritance()

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

Definition at line 17627 of file tablecmds.c.

17628{
17630 SysScanDesc scan;
17634
17635 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17637
17638 /*
17639 * Check for duplicates in the list of parents, and determine the highest
17640 * inhseqno already present; we'll use the next one for the new parent.
17641 * Also, if proposed child is a partition, it cannot already be
17642 * inheriting.
17643 *
17644 * Note: we do not reject the case where the child already inherits from
17645 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17646 */
17647 ScanKeyInit(&key,
17652 true, NULL, 1, &key);
17653
17654 /* inhseqno sequences start at 1 */
17655 inhseqno = 0;
17657 {
17659
17660 if (inh->inhparent == RelationGetRelid(parent_rel))
17661 ereport(ERROR,
17663 errmsg("relation \"%s\" would be inherited from more than once",
17665
17666 if (inh->inhseqno > inhseqno)
17667 inhseqno = inh->inhseqno;
17668 }
17669 systable_endscan(scan);
17670
17671 /* Match up the columns and bump attinhcount as needed */
17673
17674 /* Match up the constraints and bump coninhcount as needed */
17676
17677 /*
17678 * OK, it looks valid. Make the catalog entries that show inheritance.
17679 */
17682 inhseqno + 1,
17684 parent_rel->rd_rel->relkind ==
17686
17687 /* Now we're done with pg_inherits */
17689}
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:3622
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 22739 of file tablecmds.c.

22741{
22743 Oid newRelId;
22746 List *colList = NIL;
22747 Oid relamId;
22751
22752 /* If the existing rel is temp, it must belong to this session. */
22754 ereport(ERROR,
22756 errmsg("cannot create as partition of temporary relation of another session"));
22757
22758 /* Look up inheritance ancestors and generate the relation schema. */
22760
22761 /* Create a tuple descriptor from the relation schema. */
22763
22764 /* Look up the access method for the new relation. */
22766
22767 /* Look up the namespace in which we are supposed to create the relation. */
22768 namespaceId =
22771 ereport(ERROR,
22773 errmsg("relation \"%s\" already exists", newPartName->relname));
22774
22775 /*
22776 * We intended to create the partition with the same persistence as the
22777 * parent table, but we still need to recheck because that might be
22778 * affected by the search_path. If the parent is permanent, so must be
22779 * all of its partitions.
22780 */
22781 if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22782 newPartName->relpersistence == RELPERSISTENCE_TEMP)
22783 ereport(ERROR,
22785 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
22787
22788 /* Permanent rels cannot be partitions belonging to a temporary parent. */
22789 if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
22790 parent_relform->relpersistence == RELPERSISTENCE_TEMP)
22791 ereport(ERROR,
22793 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
22795
22796 /* Create the relation. */
22799 parent_relform->reltablespace,
22800 InvalidOid,
22801 InvalidOid,
22802 InvalidOid,
22803 ownerId,
22804 relamId,
22805 descriptor,
22806 NIL,
22808 newPartName->relpersistence,
22809 false,
22810 false,
22812 (Datum) 0,
22813 true,
22815 true,
22816 InvalidOid,
22817 NULL);
22818
22819 /*
22820 * We must bump the command counter to make the newly-created relation
22821 * tuple visible for opening.
22822 */
22824
22825 /*
22826 * Open the new partition with no lock, because we already have an
22827 * AccessExclusiveLock placed there after creation.
22828 */
22830
22831 /* Find or create a work queue entry for the newly created table. */
22833
22834 /* Create constraints, default values, and generated values. */
22836
22837 /*
22838 * Need to call CommandCounterIncrement, so a fresh relcache entry has
22839 * newly installed constraint info.
22840 */
22842
22843 return newRel;
22844}
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:1140
@ 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 23463 of file tablecmds.c.

23464{
23466
23468 pc->partRel = partRel;
23469
23470 /*
23471 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
23472 * don't bother using it.
23473 */
23474 pc->bistate = GetBulkInsertState();
23475
23476 /* Create a destination tuple slot for the new partition. */
23477 pc->dstslot = table_slot_create(pc->partRel, NULL);
23478
23479 return pc;
23480}
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 22532 of file tablecmds.c.

22534{
22536 TupleConstr *constr;
22537 AttrMap *attmap;
22539 int ccnum;
22540 List *constraints = NIL;
22542
22544 constr = tupleDesc->constr;
22545
22546 if (!constr)
22547 return;
22548
22549 /*
22550 * Construct a map from the parent relation's attnos to the child rel's.
22551 * This re-checks type match, etc, although it shouldn't be possible to
22552 * have a failure since both tables are locked.
22553 */
22555 tupleDesc,
22556 false);
22557
22558 /* Cycle for default values. */
22559 for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
22560 {
22562 parent_attno - 1);
22563
22564 /* Ignore dropped columns in the parent. */
22565 if (attribute->attisdropped)
22566 continue;
22567
22568 /* Copy the default, if present, and it should be copied. */
22569 if (attribute->atthasdef)
22570 {
22572 bool found_whole_row;
22573 AttrNumber num;
22574 Node *def;
22576
22577 if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
22579 else
22580 {
22582 if (this_default == NULL)
22583 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
22585 }
22586
22587 num = attmap->attnums[parent_attno - 1];
22588 def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
22589
22590 if (found_whole_row && attribute->attgenerated != '\0')
22591 elog(ERROR, "cannot convert whole-row table reference");
22592
22593 /* Add a pre-cooked default expression. */
22594 StoreAttrDefault(newRel, num, def, true);
22595
22596 /*
22597 * Stored generated column expressions in parent_rel might
22598 * reference the tableoid. newRel, parent_rel tableoid clear is
22599 * not the same. If so, these stored generated columns require
22600 * recomputation for newRel within MergePartitionsMoveRows.
22601 */
22602 if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
22603 {
22605 newval->attnum = num;
22606 newval->expr = expression_planner((Expr *) def);
22607 newval->is_generated = (attribute->attgenerated != '\0');
22608 tab->newvals = lappend(tab->newvals, newval);
22609 }
22610 }
22611 }
22612
22613 /* Cycle for CHECK constraints. */
22614 for (ccnum = 0; ccnum < constr->num_check; ccnum++)
22615 {
22616 char *ccname = constr->check[ccnum].ccname;
22617 char *ccbin = constr->check[ccnum].ccbin;
22618 bool ccenforced = constr->check[ccnum].ccenforced;
22619 bool ccnoinherit = constr->check[ccnum].ccnoinherit;
22620 bool ccvalid = constr->check[ccnum].ccvalid;
22622 bool found_whole_row;
22623 Constraint *con;
22624
22625 /*
22626 * The partitioned table can not have a NO INHERIT check constraint
22627 * (see StoreRelCheck function for details).
22628 */
22629 Assert(!ccnoinherit);
22630
22632 1, 0,
22633 attmap,
22634 InvalidOid, &found_whole_row);
22635
22636 /*
22637 * For the moment we have to reject whole-row variables (as for CREATE
22638 * TABLE LIKE and inheritances).
22639 */
22640 if (found_whole_row)
22641 elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
22642 ccname,
22644
22645 con = makeNode(Constraint);
22646 con->contype = CONSTR_CHECK;
22647 con->conname = pstrdup(ccname);
22648 con->deferrable = false;
22649 con->initdeferred = false;
22650 con->is_enforced = ccenforced;
22651 con->skip_validation = !ccvalid;
22652 con->initially_valid = ccvalid;
22653 con->is_no_inherit = ccnoinherit;
22654 con->raw_expr = NULL;
22656 con->location = -1;
22657 constraints = lappend(constraints, con);
22658 }
22659
22660 /* Install all CHECK constraints. */
22662 false, true, true, NULL);
22663
22664 /* Make the additional catalog changes visible. */
22666
22667 /*
22668 * parent_rel check constraint expression may reference tableoid, so later
22669 * in MergePartitionsMoveRows, we need to evaluate the check constraint
22670 * again for the newRel. We can check whether the check constraint
22671 * contains a tableoid reference via pull_varattnos.
22672 */
22674 {
22675 if (!ccon->skip_validation)
22676 {
22677 Node *qual;
22678 Bitmapset *attnums = NULL;
22679
22680 Assert(ccon->contype == CONSTR_CHECK);
22682 pull_varattnos(qual, 1, &attnums);
22683
22684 /*
22685 * Add a check only if it contains a tableoid
22686 * (TableOidAttributeNumber).
22687 */
22689 attnums))
22690 {
22692
22694 newcon->name = ccon->name;
22695 newcon->contype = CONSTR_CHECK;
22696 newcon->qual = qual;
22697
22698 tab->constraints = lappend(tab->constraints, newcon);
22699 }
22700 }
22701 }
22702
22703 /* Don't need the cookedConstraints anymore. */
22705
22706 /* Reproduce not-null constraints. */
22707 if (constr->has_not_null)
22708 {
22709 List *nnconstraints;
22710
22711 /*
22712 * The "include_noinh" argument is false because a partitioned table
22713 * can't have NO INHERIT constraint.
22714 */
22716 false, false);
22717
22718 Assert(list_length(nnconstraints) > 0);
22719
22720 /*
22721 * We already set pg_attribute.attnotnull in createPartitionTable. No
22722 * need call set_attnotnull again.
22723 */
22724 AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, true, NULL);
22725 }
22726}
void list_free_deep(List *list)
Definition list.c:1560
char * nodeToString(const void *obj)
Definition outfuncs.c:811
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
bool has_not_null
Definition tupdesc.h:45
#define TableOidAttributeNumber
Definition sysattr.h:26
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition tupdesc.c:1161

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

17697{
17699 bool isnull;
17700 Datum attr;
17701 Datum expr;
17702
17704 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17705 if (isnull)
17706 elog(ERROR, "null conbin for constraint %u", con->oid);
17707
17708 expr = DirectFunctionCall2(pg_get_expr, attr,
17709 ObjectIdGetDatum(con->conrelid));
17710 return TextDatumGetCString(expr);
17711}
#define DirectFunctionCall2(func, arg1, arg2)
Definition fmgr.h:690
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition ruleutils.c:3033

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

812{
813 char relname[NAMEDATALEN];
817 Relation rel;
824 List *nncols;
825 List *connames = NIL;
826 Datum reloptions;
829 bool partitioned;
830 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
832 ObjectAddress address;
835
836 /*
837 * Truncate relname to appropriate length (probably a waste of time, as
838 * parser should have done this already).
839 */
840 strlcpy(relname, stmt->relation->relname, NAMEDATALEN);
841
842 /*
843 * Check consistency of arguments
844 */
845 if (stmt->oncommit != ONCOMMIT_NOOP
846 && stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
849 errmsg("ON COMMIT can only be used on temporary tables")));
850
851 if (stmt->partspec != NULL)
852 {
853 if (relkind != RELKIND_RELATION)
854 elog(ERROR, "unexpected relkind: %d", (int) relkind);
855
857 partitioned = true;
858 }
859 else
860 partitioned = false;
861
862 if (relkind == RELKIND_PARTITIONED_TABLE &&
863 stmt->relation->relpersistence == RELPERSISTENCE_UNLOGGED)
866 errmsg("partitioned tables cannot be unlogged")));
867
868 /*
869 * Look up the namespace in which we are supposed to create the relation,
870 * check we have permission to create there, lock it against concurrent
871 * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary
872 * namespace is selected.
873 */
876
877 /*
878 * Security check: disallow creating temp tables from security-restricted
879 * code. This is needed because calling code might not expect untrusted
880 * tables to appear in pg_temp at the front of its search path.
881 */
882 if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP
886 errmsg("cannot create temporary table within security-restricted operation")));
887
888 /*
889 * Determine the lockmode to use when scanning parents. A self-exclusive
890 * lock is needed here.
891 *
892 * For regular inheritance, if two backends attempt to add children to the
893 * same parent simultaneously, and that parent has no pre-existing
894 * children, then both will attempt to update the parent's relhassubclass
895 * field, leading to a "tuple concurrently updated" error. Also, this
896 * interlocks against a concurrent ANALYZE on the parent table, which
897 * might otherwise be attempting to clear the parent's relhassubclass
898 * field, if its previous children were recently dropped.
899 *
900 * If the child table is a partition, then we instead grab an exclusive
901 * lock on the parent because its partition descriptor will be changed by
902 * addition of the new partition.
903 */
904 parentLockmode = (stmt->partbound != NULL ? AccessExclusiveLock :
906
907 /* Determine the list of OIDs of the parents. */
909 foreach(listptr, stmt->inhRelations)
910 {
911 RangeVar *rv = (RangeVar *) lfirst(listptr);
913
915
916 /*
917 * Reject duplications in the list of parents.
918 */
922 errmsg("relation \"%s\" would be inherited from more than once",
924
926 }
927
928 /*
929 * Select tablespace to use: an explicitly indicated one, or (in the case
930 * of a partitioned table) the parent's, if it has one.
931 */
932 if (stmt->tablespacename)
933 {
934 tablespaceId = get_tablespace_oid(stmt->tablespacename, false);
935
939 errmsg("cannot specify default tablespace for partitioned relations")));
940 }
941 else if (stmt->partbound)
942 {
945 }
946 else
948
949 /* still nothing? use the default */
951 tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence,
953
954 /* Check permissions except when using database's default */
956 {
958
960 ACL_CREATE);
961 if (aclresult != ACLCHECK_OK)
964 }
965
966 /* In all cases disallow placing user relations in pg_global */
970 errmsg("only shared relations can be placed in pg_global tablespace")));
971
972 /* Identify user ID that will own the table */
973 if (!OidIsValid(ownerId))
974 ownerId = GetUserId();
975
976 /*
977 * Parse and validate reloptions, if any.
978 */
979 reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps,
980 true, false);
981
982 switch (relkind)
983 {
984 case RELKIND_VIEW:
985 (void) view_reloptions(reloptions, true);
986 break;
988 (void) partitioned_table_reloptions(reloptions, true);
989 break;
990 default:
991 (void) heap_reloptions(relkind, reloptions, true);
992 }
993
994 if (stmt->ofTypename)
995 {
997
998 ofTypeId = typenameTypeId(NULL, stmt->ofTypename);
999
1001 if (aclresult != ACLCHECK_OK)
1003 }
1004 else
1006
1007 /*
1008 * Look up inheritance ancestors and generate relation schema, including
1009 * inherited attributes. (Note that stmt->tableElts is destructively
1010 * modified by MergeAttributes.)
1011 */
1012 stmt->tableElts =
1013 MergeAttributes(stmt->tableElts, inheritOids,
1014 stmt->relation->relpersistence,
1015 stmt->partbound != NULL,
1017
1018 /*
1019 * Create a tuple descriptor from the relation schema. Note that this
1020 * deals with column names, types, and in-descriptor NOT NULL flags, but
1021 * not default values, NOT NULL or CHECK constraints; we handle those
1022 * below.
1023 */
1024 descriptor = BuildDescForRelation(stmt->tableElts);
1025
1026 /*
1027 * Find columns with default values and prepare for insertion of the
1028 * defaults. Pre-cooked (that is, inherited) defaults go into a list of
1029 * CookedConstraint structs that we'll pass to heap_create_with_catalog,
1030 * while raw defaults go into a list of RawColumnDefault structs that will
1031 * be processed by AddRelationNewConstraints. (We can't deal with raw
1032 * expressions until we can do transformExpr.)
1033 */
1034 rawDefaults = NIL;
1036 attnum = 0;
1037
1038 foreach(listptr, stmt->tableElts)
1039 {
1041
1042 attnum++;
1043 if (colDef->raw_default != NULL)
1044 {
1046
1047 Assert(colDef->cooked_default == NULL);
1048
1050 rawEnt->attnum = attnum;
1051 rawEnt->raw_default = colDef->raw_default;
1052 rawEnt->generated = colDef->generated;
1054 }
1055 else if (colDef->cooked_default != NULL)
1056 {
1058
1060 cooked->contype = CONSTR_DEFAULT;
1061 cooked->conoid = InvalidOid; /* until created */
1062 cooked->name = NULL;
1063 cooked->attnum = attnum;
1064 cooked->expr = colDef->cooked_default;
1065 cooked->is_enforced = true;
1066 cooked->skip_validation = false;
1067 cooked->is_local = true; /* not used for defaults */
1068 cooked->inhcount = 0; /* ditto */
1069 cooked->is_no_inherit = false;
1071 }
1072 }
1073
1075
1076 /*
1077 * For relations with table AM and partitioned tables, select access
1078 * method to use: an explicitly indicated one, or (in the case of a
1079 * partitioned table) the parent's, if it has one.
1080 */
1081 if (stmt->accessMethod != NULL)
1082 {
1084 accessMethodId = get_table_am_oid(stmt->accessMethod, false);
1085 }
1086 else if (RELKIND_HAS_TABLE_AM(relkind) || relkind == RELKIND_PARTITIONED_TABLE)
1087 {
1088 if (stmt->partbound)
1089 {
1092 }
1093
1096 }
1097
1098 /*
1099 * Create the relation. Inherited defaults and CHECK constraints are
1100 * passed in for immediate handling --- since they don't need parsing,
1101 * they can be stored immediately.
1102 */
1106 InvalidOid,
1107 InvalidOid,
1108 ofTypeId,
1109 ownerId,
1111 descriptor,
1114 relkind,
1115 stmt->relation->relpersistence,
1116 false,
1117 false,
1118 stmt->oncommit,
1119 reloptions,
1120 true,
1122 false,
1123 InvalidOid,
1124 typaddress);
1125
1126 /*
1127 * We must bump the command counter to make the newly-created relation
1128 * tuple visible for opening.
1129 */
1131
1132 /*
1133 * Open the new relation and acquire exclusive lock on it. This isn't
1134 * really necessary for locking out other backends (since they can't see
1135 * the new rel anyway until we commit), but it keeps the lock manager from
1136 * complaining about deadlock risks.
1137 */
1139
1140 /*
1141 * Now add any newly specified column default and generation expressions
1142 * to the new relation. These are passed to us in the form of raw
1143 * parsetrees; we need to transform them to executable expression trees
1144 * before they can be added. The most convenient way to do that is to
1145 * apply the parser's transformExpr routine, but transformExpr doesn't
1146 * work unless we have a pre-existing relation. So, the transformation has
1147 * to be postponed to this final step of CREATE TABLE.
1148 *
1149 * This needs to be before processing the partitioning clauses because
1150 * those could refer to generated columns.
1151 */
1152 if (rawDefaults)
1154 true, true, false, queryString);
1155
1156 /*
1157 * Make column generation expressions visible for use by partitioning.
1158 */
1160
1161 /* Process and store partition bound, if any. */
1162 if (stmt->partbound)
1163 {
1164 PartitionBoundSpec *bound;
1165 ParseState *pstate;
1168 Relation parent,
1169 defaultRel = NULL;
1171
1172 /* Already have strong enough lock on the parent */
1173 parent = table_open(parentId, NoLock);
1174
1175 /*
1176 * We are going to try to validate the partition bound specification
1177 * against the partition key of parentRel, so it better have one.
1178 */
1179 if (parent->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
1180 ereport(ERROR,
1182 errmsg("\"%s\" is not partitioned",
1183 RelationGetRelationName(parent))));
1184
1185 /*
1186 * The partition constraint of the default partition depends on the
1187 * partition bounds of every other partition. It is possible that
1188 * another backend might be about to execute a query on the default
1189 * partition table, and that the query relies on previously cached
1190 * default partition constraints. We must therefore take a table lock
1191 * strong enough to prevent all queries on the default partition from
1192 * proceeding until we commit and send out a shared-cache-inval notice
1193 * that will make them update their index lists.
1194 *
1195 * Order of locking: The relation being added won't be visible to
1196 * other backends until it is committed, hence here in
1197 * DefineRelation() the order of locking the default partition and the
1198 * relation being added does not matter. But at all other places we
1199 * need to lock the default relation before we lock the relation being
1200 * added or removed i.e. we should take the lock in same order at all
1201 * the places such that lock parent, lock default partition and then
1202 * lock the partition so as to avoid a deadlock.
1203 */
1206 true));
1209
1210 /* Transform the bound values */
1211 pstate = make_parsestate(NULL);
1212 pstate->p_sourcetext = queryString;
1213
1214 /*
1215 * Add an nsitem containing this relation, so that transformExpr
1216 * called on partition bound expressions is able to report errors
1217 * using a proper context.
1218 */
1220 NULL, false, false);
1221 addNSItemToQuery(pstate, nsitem, false, true, true);
1222
1223 bound = transformPartitionBound(pstate, parent, stmt->partbound);
1224
1225 /*
1226 * Check first that the new partition's bound is valid and does not
1227 * overlap with any of existing partitions of the parent.
1228 */
1229 check_new_partition_bound(relname, parent, bound, pstate);
1230
1231 /*
1232 * If the default partition exists, its partition constraints will
1233 * change after the addition of this new partition such that it won't
1234 * allow any row that qualifies for this new partition. So, check that
1235 * the existing data in the default partition satisfies the constraint
1236 * as it will exist after adding this partition.
1237 */
1239 {
1241 /* Keep the lock until commit. */
1243 }
1244
1245 /* Update the pg_class entry. */
1246 StorePartitionBound(rel, parent, bound);
1247
1248 table_close(parent, NoLock);
1249 }
1250
1251 /* Store inheritance information for new rel. */
1253
1254 /*
1255 * Process the partitioning specification (if any) and store the partition
1256 * key information into the catalog.
1257 */
1258 if (partitioned)
1259 {
1260 ParseState *pstate;
1261 int partnatts;
1262 AttrNumber partattrs[PARTITION_MAX_KEYS];
1264 Oid partcollation[PARTITION_MAX_KEYS];
1265 List *partexprs = NIL;
1266
1267 pstate = make_parsestate(NULL);
1268 pstate->p_sourcetext = queryString;
1269
1270 partnatts = list_length(stmt->partspec->partParams);
1271
1272 /* Protect fixed-size arrays here and in executor */
1273 if (partnatts > PARTITION_MAX_KEYS)
1274 ereport(ERROR,
1276 errmsg("cannot partition using more than %d columns",
1278
1279 /*
1280 * We need to transform the raw parsetrees corresponding to partition
1281 * expressions into executable expression trees. Like column defaults
1282 * and CHECK constraints, we could not have done the transformation
1283 * earlier.
1284 */
1285 stmt->partspec = transformPartitionSpec(rel, stmt->partspec);
1286
1287 ComputePartitionAttrs(pstate, rel, stmt->partspec->partParams,
1288 partattrs, &partexprs, partopclass,
1289 partcollation, stmt->partspec->strategy);
1290
1291 StorePartitionKey(rel, stmt->partspec->strategy, partnatts, partattrs,
1292 partexprs,
1293 partopclass, partcollation);
1294
1295 /* make it all visible */
1297 }
1298
1299 /*
1300 * If we're creating a partition, create now all the indexes, triggers,
1301 * FKs defined in the parent.
1302 *
1303 * We can't do it earlier, because DefineIndex wants to know the partition
1304 * key which we just stored.
1305 */
1306 if (stmt->partbound)
1307 {
1309 Relation parent;
1310 List *idxlist;
1311 ListCell *cell;
1312
1313 /* Already have strong enough lock on the parent */
1314 parent = table_open(parentId, NoLock);
1315 idxlist = RelationGetIndexList(parent);
1316
1317 /*
1318 * For each index in the parent table, create one in the partition
1319 */
1320 foreach(cell, idxlist)
1321 {
1323 AttrMap *attmap;
1326
1327 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1328 {
1329 if (idxRel->rd_index->indisunique)
1330 ereport(ERROR,
1332 errmsg("cannot create foreign partition of partitioned table \"%s\"",
1333 RelationGetRelationName(parent)),
1334 errdetail("Table \"%s\" contains indexes that are unique.",
1335 RelationGetRelationName(parent))));
1336 else
1337 {
1339 continue;
1340 }
1341 }
1342
1344 RelationGetDescr(parent),
1345 false);
1346 idxstmt =
1350 RelationGetRelid(rel),
1351 idxstmt,
1352 InvalidOid,
1355 -1,
1356 false, false, false, false, false);
1357
1359 }
1360
1362
1363 /*
1364 * If there are any row-level triggers, clone them to the new
1365 * partition.
1366 */
1367 if (parent->trigdesc != NULL)
1368 CloneRowTriggersToPartition(parent, rel);
1369
1370 /*
1371 * And foreign keys too. Note that because we're freshly creating the
1372 * table, there is no need to verify these new constraints.
1373 */
1374 CloneForeignKeyConstraints(NULL, parent, rel);
1375
1376 table_close(parent, NoLock);
1377 }
1378
1379 /*
1380 * Now add any newly specified CHECK constraints to the new relation. Same
1381 * as for defaults above, but these need to come after partitioning is set
1382 * up. We save the constraint names that were used, to avoid dupes below.
1383 */
1384 if (stmt->constraints)
1385 {
1386 List *conlist;
1387
1388 conlist = AddRelationNewConstraints(rel, NIL, stmt->constraints,
1389 true, true, false, queryString);
1391 {
1392 if (cons->name != NULL)
1393 connames = lappend(connames, cons->name);
1394 }
1395 }
1396
1397 /*
1398 * Finally, merge the not-null constraints that are declared directly with
1399 * those that come from parent relations (making sure to count inheritance
1400 * appropriately for each), create them, and set the attnotnull flag on
1401 * columns that don't yet have it.
1402 */
1403 nncols = AddRelationNotNullConstraints(rel, stmt->nnconstraints,
1405 foreach_int(attrnum, nncols)
1406 set_attnotnull(NULL, rel, attrnum, true, false);
1407
1409
1410 /*
1411 * Clean up. We keep lock on new relation (although it shouldn't be
1412 * visible to anyone else anyway, until commit).
1413 */
1414 relation_close(rel, NoLock);
1415
1416 return address;
1417}
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:3918
List * AddRelationNotNullConstraints(Relation rel, List *constraints, List *old_notnulls, List *existing_constraints)
Definition heap.c:2917
Oid get_rel_relam(Oid relid)
Definition lsyscache.c:2406
Oid get_rel_tablespace(Oid relid)
Definition lsyscache.c:2360
bool InSecurityRestrictedOperation(void)
Definition miscinit.c:640
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:3578
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:2604

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, TupleDescFinalize(), typenameTypeId(), and view_reloptions().

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

◆ deleteSplitPartitionContext()

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

Definition at line 23486 of file tablecmds.c.

23487{
23488 ListCell *ltab;
23489
23491 FreeBulkInsertState(pc->bistate);
23492
23493 table_finish_bulk_insert(pc->partRel, ti_options);
23494
23495 /*
23496 * We don't need to process this pc->partRel so delete the ALTER TABLE
23497 * queue of it.
23498 */
23499 foreach(ltab, *wqueue)
23500 {
23502
23503 if (tab->relid == RelationGetRelid(pc->partRel))
23504 {
23506 break;
23507 }
23508 }
23509
23510 pfree(pc);
23511}
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 21400 of file tablecmds.c.

21402{
21404 List *fks;
21405 ListCell *cell;
21406 List *indexes;
21410 HeapTuple tuple,
21411 newtuple;
21413 List *fkoids = NIL;
21414
21415 if (concurrent)
21416 {
21417 /*
21418 * We can remove the pg_inherits row now. (In the non-concurrent case,
21419 * this was already done).
21420 */
21421 RemoveInheritance(partRel, rel, true);
21422 }
21423
21424 /* Drop any triggers that were cloned on creation/attach. */
21426
21427 /*
21428 * Detach any foreign keys that are inherited. This includes creating
21429 * additional action triggers.
21430 */
21432 if (fks != NIL)
21434
21435 /*
21436 * It's possible that the partition being detached has a foreign key that
21437 * references a partitioned table. When that happens, there are multiple
21438 * pg_constraint rows for the partition: one points to the partitioned
21439 * table itself, while the others point to each of its partitions. Only
21440 * the topmost one is to be considered here; the child constraints must be
21441 * left alone, because conceptually those aren't coming from our parent
21442 * partitioned table, but from this partition itself.
21443 *
21444 * We implement this by collecting all the constraint OIDs in a first scan
21445 * of the FK array, and skipping in the loop below those constraints whose
21446 * parents are listed here.
21447 */
21449 fkoids = lappend_oid(fkoids, fk->conoid);
21450
21451 foreach(cell, fks)
21452 {
21453 ForeignKeyCacheInfo *fk = lfirst(cell);
21456
21459 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21461
21462 /*
21463 * Consider only inherited foreign keys, and only if their parents
21464 * aren't in the list.
21465 */
21466 if (conform->contype != CONSTRAINT_FOREIGN ||
21467 !OidIsValid(conform->conparentid) ||
21468 list_member_oid(fkoids, conform->conparentid))
21469 {
21471 continue;
21472 }
21473
21474 /*
21475 * The constraint on this table must be marked no longer a child of
21476 * the parent's constraint, as do its check triggers.
21477 */
21479
21480 /*
21481 * Also, look up the partition's "check" triggers corresponding to the
21482 * ENFORCED constraint being detached and detach them from the parent
21483 * triggers. NOT ENFORCED constraints do not have these triggers;
21484 * therefore, this step is not needed.
21485 */
21486 if (fk->conenforced)
21487 {
21490
21492 fk->conoid, fk->confrelid, fk->conrelid,
21496 RelationGetRelid(partRel));
21499 RelationGetRelid(partRel));
21500 }
21501
21502 /*
21503 * Lastly, create the action triggers on the referenced table, using
21504 * addFkRecurseReferenced, which requires some elaborate setup (so put
21505 * it in a separate block). While at it, if the table is partitioned,
21506 * that function will recurse to create the pg_constraint rows and
21507 * action triggers for each partition.
21508 *
21509 * Note there's no need to do addFkConstraint() here, because the
21510 * pg_constraint row already exists.
21511 */
21512 {
21514 int numfks;
21520 int numfkdelsetcols;
21521 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21523
21525 &numfks,
21526 conkey,
21527 confkey,
21528 conpfeqop,
21529 conppeqop,
21530 conffeqop,
21532 confdelsetcols);
21533
21534 /* Create a synthetic node we'll use throughout */
21537 fkconstraint->conname = pstrdup(NameStr(conform->conname));
21538 fkconstraint->deferrable = conform->condeferrable;
21539 fkconstraint->initdeferred = conform->condeferred;
21540 fkconstraint->is_enforced = conform->conenforced;
21541 fkconstraint->skip_validation = true;
21542 fkconstraint->initially_valid = conform->convalidated;
21543 /* a few irrelevant fields omitted here */
21544 fkconstraint->pktable = NULL;
21545 fkconstraint->fk_attrs = NIL;
21546 fkconstraint->pk_attrs = NIL;
21547 fkconstraint->fk_matchtype = conform->confmatchtype;
21548 fkconstraint->fk_upd_action = conform->confupdtype;
21549 fkconstraint->fk_del_action = conform->confdeltype;
21550 fkconstraint->fk_del_set_cols = NIL;
21551 fkconstraint->old_conpfeqop = NIL;
21552 fkconstraint->old_pktable_oid = InvalidOid;
21553 fkconstraint->location = -1;
21554
21555 /* set up colnames, used to generate the constraint name */
21556 for (int i = 0; i < numfks; i++)
21557 {
21559
21560 att = TupleDescAttr(RelationGetDescr(partRel),
21561 conkey[i] - 1);
21562
21563 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21564 makeString(NameStr(att->attname)));
21565 }
21566
21568
21570 refdRel,
21571 conform->conindid,
21572 fk->conoid,
21573 numfks,
21574 confkey,
21575 conkey,
21576 conpfeqop,
21577 conppeqop,
21578 conffeqop,
21580 confdelsetcols,
21581 true,
21583 conform->conperiod);
21584 table_close(refdRel, NoLock); /* keep lock till end of xact */
21585 }
21586
21588 }
21590 if (trigrel)
21592
21593 /*
21594 * Any sub-constraints that are in the referenced-side of a larger
21595 * constraint have to be removed. This partition is no longer part of the
21596 * key space of the constraint.
21597 */
21598 foreach(cell, GetParentedForeignKeyRefs(partRel))
21599 {
21600 Oid constrOid = lfirst_oid(cell);
21601 ObjectAddress constraint;
21602
21605 constrOid,
21609
21611 performDeletion(&constraint, DROP_RESTRICT, 0);
21612 }
21613
21614 /* Now we can detach indexes */
21615 indexes = RelationGetIndexList(partRel);
21616 foreach(cell, indexes)
21617 {
21618 Oid idxid = lfirst_oid(cell);
21619 Oid parentidx;
21620 Relation idx;
21621 Oid constrOid;
21623
21624 if (!has_superclass(idxid))
21625 continue;
21626
21627 parentidx = get_partition_parent(idxid, false);
21628 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21629
21632
21633 /*
21634 * If there's a constraint associated with the index, detach it too.
21635 * Careful: it is possible for a constraint index in a partition to be
21636 * the child of a non-constraint index, so verify whether the parent
21637 * index does actually have a constraint.
21638 */
21640 idxid);
21642 parentidx);
21645
21647 }
21648
21649 /* Update pg_class tuple */
21653 if (!HeapTupleIsValid(tuple))
21654 elog(ERROR, "cache lookup failed for relation %u",
21655 RelationGetRelid(partRel));
21657
21658 /* Clear relpartbound and reset relispartition */
21659 memset(new_val, 0, sizeof(new_val));
21660 memset(new_null, false, sizeof(new_null));
21661 memset(new_repl, false, sizeof(new_repl));
21665 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21667
21668 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21669 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21670 heap_freetuple(newtuple);
21672
21673 /*
21674 * Drop identity property from all identity columns of partition.
21675 */
21676 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21677 {
21678 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21679
21680 if (!attr->attisdropped && attr->attidentity)
21681 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21682 AccessExclusiveLock, true, true);
21683 }
21684
21686 {
21687 /*
21688 * If the relation being detached is the default partition itself,
21689 * remove it from the parent's pg_partitioned_table entry.
21690 *
21691 * If not, we must invalidate default partition's relcache entry, as
21692 * in StorePartitionBound: its partition constraint depends on every
21693 * other partition's partition constraint.
21694 */
21695 if (RelationGetRelid(partRel) == defaultPartOid)
21697 else
21699 }
21700
21701 /*
21702 * Invalidate the parent's relcache so that the partition is no longer
21703 * included in its partition descriptor.
21704 */
21706
21707 /*
21708 * If the partition we just detached is partitioned itself, invalidate
21709 * relcache for all descendent partitions too to ensure that their
21710 * rd_partcheck expression trees are rebuilt; must lock partitions before
21711 * doing so, using the same lockmode as what partRel has been locked with
21712 * by the caller.
21713 */
21714 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21715 {
21716 List *children;
21717
21718 children = find_all_inheritors(RelationGetRelid(partRel),
21720 foreach(cell, children)
21721 {
21723 }
21724 }
21725}
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 23007 of file tablecmds.c.

23008{
23009 /* Remove the pg_inherits row first. */
23011
23012 /*
23013 * Detaching the partition might involve TOAST table access, so ensure we
23014 * have a valid snapshot.
23015 */
23017
23018 /* Do the final part of detaching. */
23020
23022}

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

18414{
18416 SysScanDesc scan;
18417 ScanKeyData key[3];
18419
18421
18422 ScanKeyInit(&key[0],
18426 ScanKeyInit(&key[1],
18429 ObjectIdGetDatum(relid));
18430 ScanKeyInit(&key[2],
18433 Int32GetDatum(0));
18434
18436 NULL, 3, key);
18437
18439 {
18441
18442 if (dep->refclassid == refclassid &&
18443 dep->refobjid == refobjid &&
18444 dep->refobjsubid == 0 &&
18445 dep->deptype == deptype)
18447 }
18448
18449 systable_endscan(scan);
18451}

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

21769{
21771 SysScanDesc scan;
21774 ObjectAddresses *objects;
21775
21776 objects = new_object_addresses();
21777
21778 /*
21779 * Scan pg_trigger to search for all triggers on this rel.
21780 */
21785 true, NULL, 1, &skey);
21787 {
21790
21791 /* Ignore triggers that weren't cloned */
21792 if (!OidIsValid(pg_trigger->tgparentid))
21793 continue;
21794
21795 /*
21796 * Ignore internal triggers that are implementation objects of foreign
21797 * keys, because these will be detached when the foreign keys
21798 * themselves are.
21799 */
21800 if (OidIsValid(pg_trigger->tgconstrrelid))
21801 continue;
21802
21803 /*
21804 * This is ugly, but necessary: remove the dependency markings on the
21805 * trigger so that it can be removed.
21806 */
21813
21814 /* remember this trigger to remove it below */
21816 add_exact_object_address(&trig, objects);
21817 }
21818
21819 /* make the dependency removal visible to the deletion below */
21822
21823 /* done */
21824 free_object_addresses(objects);
21825 systable_endscan(scan);
21827}

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

14336{
14340 List *children;
14341 bool is_no_inherit_constraint = false;
14342 char *constrName;
14343 char *colname = NULL;
14344
14345 /* Guard against stack overflow due to overly deep inheritance tree. */
14347
14348 /* At top level, permission check was done in ATPrepCmd, else do it */
14349 if (recursing)
14352
14354
14356 constrName = NameStr(con->conname);
14357
14358 /* Don't allow drop of inherited constraints */
14359 if (con->coninhcount > 0 && !recursing)
14360 ereport(ERROR,
14362 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14364
14365 /*
14366 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14367 *
14368 * While doing that, we're in a good position to disallow dropping a not-
14369 * null constraint underneath a primary key, a replica identity index, or
14370 * a generated identity column.
14371 */
14372 if (con->contype == CONSTRAINT_NOTNULL)
14373 {
14380
14381 /* save column name for recursion step */
14382 colname = get_attname(RelationGetRelid(rel), attnum, false);
14383
14384 /*
14385 * Disallow if it's in the primary key. For partitioned tables we
14386 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14387 * return NULL if the primary key is invalid; but we still need to
14388 * protect not-null constraints under such a constraint, so check the
14389 * slow way.
14390 */
14392
14393 if (pkattrs == NULL &&
14394 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14395 {
14397
14398 if (OidIsValid(pkindex))
14399 {
14401
14402 pkattrs = NULL;
14403 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14404 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14405
14407 }
14408 }
14409
14410 if (pkattrs &&
14412 ereport(ERROR,
14414 errmsg("column \"%s\" is in a primary key",
14415 get_attname(RelationGetRelid(rel), attnum, false)));
14416
14417 /* Disallow if it's in the replica identity */
14420 ereport(ERROR,
14422 errmsg("column \"%s\" is in index used as replica identity",
14423 get_attname(RelationGetRelid(rel), attnum, false)));
14424
14425 /* Disallow if it's a GENERATED AS IDENTITY column */
14428 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14429 attnum, RelationGetRelid(rel));
14431 if (attForm->attidentity != '\0')
14432 ereport(ERROR,
14434 errmsg("column \"%s\" of relation \"%s\" is an identity column",
14436 false),
14438
14439 /* All good -- reset attnotnull if needed */
14440 if (attForm->attnotnull)
14441 {
14442 attForm->attnotnull = false;
14444 }
14445
14447 }
14448
14449 is_no_inherit_constraint = con->connoinherit;
14450
14451 /*
14452 * If it's a foreign-key constraint, we'd better lock the referenced table
14453 * and check that that's not in use, just as we've already done for the
14454 * constrained table (else we might, eg, be dropping a trigger that has
14455 * unfired events). But we can/must skip that in the self-referential
14456 * case.
14457 */
14458 if (con->contype == CONSTRAINT_FOREIGN &&
14459 con->confrelid != RelationGetRelid(rel))
14460 {
14461 Relation frel;
14462
14463 /* Must match lock taken by RemoveTriggerById: */
14464 frel = table_open(con->confrelid, AccessExclusiveLock);
14467 }
14468
14469 /*
14470 * Perform the actual constraint deletion
14471 */
14473 performDeletion(&conobj, behavior, 0);
14474
14475 /*
14476 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14477 * are dropped via the dependency mechanism, so we're done here.
14478 */
14479 if (con->contype != CONSTRAINT_CHECK &&
14480 con->contype != CONSTRAINT_NOTNULL &&
14481 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14482 {
14484 return conobj;
14485 }
14486
14487 /*
14488 * Propagate to children as appropriate. Unlike most other ALTER
14489 * routines, we have to do this one level of recursion at a time; we can't
14490 * use find_all_inheritors to do it in one pass.
14491 */
14493 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14494 else
14495 children = NIL;
14496
14497 foreach_oid(childrelid, children)
14498 {
14500 HeapTuple tuple;
14502
14503 /* find_inheritance_children already got lock */
14506
14507 /*
14508 * We search for not-null constraints by column name, and others by
14509 * constraint name.
14510 */
14511 if (con->contype == CONSTRAINT_NOTNULL)
14512 {
14513 tuple = findNotNullConstraint(childrelid, colname);
14514 if (!HeapTupleIsValid(tuple))
14515 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14516 colname, RelationGetRelid(childrel));
14517 }
14518 else
14519 {
14520 SysScanDesc scan;
14521 ScanKeyData skey[3];
14522
14523 ScanKeyInit(&skey[0],
14527 ScanKeyInit(&skey[1],
14531 ScanKeyInit(&skey[2],
14536 true, NULL, 3, skey);
14537 /* There can only be one, so no need to loop */
14538 tuple = systable_getnext(scan);
14539 if (!HeapTupleIsValid(tuple))
14540 ereport(ERROR,
14542 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14543 constrName,
14545 tuple = heap_copytuple(tuple);
14546 systable_endscan(scan);
14547 }
14548
14550
14551 /* Right now only CHECK and not-null constraints can be inherited */
14552 if (childcon->contype != CONSTRAINT_CHECK &&
14553 childcon->contype != CONSTRAINT_NOTNULL)
14554 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14555
14556 if (childcon->coninhcount <= 0) /* shouldn't happen */
14557 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14558 childrelid, NameStr(childcon->conname));
14559
14560 if (recurse)
14561 {
14562 /*
14563 * If the child constraint has other definition sources, just
14564 * decrement its inheritance count; if not, recurse to delete it.
14565 */
14566 if (childcon->coninhcount == 1 && !childcon->conislocal)
14567 {
14568 /* Time to delete this child constraint, too */
14569 dropconstraint_internal(childrel, tuple, behavior,
14570 recurse, true, missing_ok,
14571 lockmode);
14572 }
14573 else
14574 {
14575 /* Child constraint must survive my deletion */
14576 childcon->coninhcount--;
14577 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14578
14579 /* Make update visible */
14581 }
14582 }
14583 else
14584 {
14585 /*
14586 * If we were told to drop ONLY in this table (no recursion) and
14587 * there are no further parents for this constraint, we need to
14588 * mark the inheritors' constraints as locally defined rather than
14589 * inherited.
14590 */
14591 childcon->coninhcount--;
14592 if (childcon->coninhcount == 0)
14593 childcon->conislocal = true;
14594
14595 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14596
14597 /* Make update visible */
14599 }
14600
14601 heap_freetuple(tuple);
14602
14604 }
14605
14607
14608 return conobj;
14609}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
Definition relcache.c:5048
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition relcache.c:5304
@ INDEX_ATTR_BITMAP_PRIMARY_KEY
Definition relcache.h:71
@ INDEX_ATTR_BITMAP_IDENTITY_KEY
Definition relcache.h:72
HeapTuple SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
Definition syscache.c:562

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

1515{
1516 const struct dropmsgstrings *rentry;
1517
1518 if (rel->schemaname != NULL &&
1520 {
1521 if (!missing_ok)
1522 {
1523 ereport(ERROR,
1525 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1526 }
1527 else
1528 {
1530 (errmsg("schema \"%s\" does not exist, skipping",
1531 rel->schemaname)));
1532 }
1533 return;
1534 }
1535
1536 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1537 {
1538 if (rentry->kind == rightkind)
1539 {
1540 if (!missing_ok)
1541 {
1542 ereport(ERROR,
1543 (errcode(rentry->nonexistent_code),
1544 errmsg(rentry->nonexistent_msg, rel->relname)));
1545 }
1546 else
1547 {
1548 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1549 break;
1550 }
1551 }
1552 }
1553
1554 Assert(rentry->kind != '\0'); /* Should be impossible */
1555}
Oid LookupNamespaceNoError(const char *nspname)
Definition namespace.c:3427
char * schemaname
Definition primnodes.h:81
static const struct dropmsgstrings dropmsgstringarray[]
Definition tablecmds.c:259

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

1563{
1564 const struct dropmsgstrings *rentry;
1565 const struct dropmsgstrings *wentry;
1566
1567 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1568 if (rentry->kind == rightkind)
1569 break;
1570 Assert(rentry->kind != '\0');
1571
1572 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1573 if (wentry->kind == wrongkind)
1574 break;
1575 /* wrongkind could be something we don't have in our table... */
1576
1577 ereport(ERROR,
1579 errmsg(rentry->nota_msg, relname),
1580 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1581}

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

12083{
12085 SysScanDesc scan;
12087
12088 ScanKeyInit(&key,
12091 ObjectIdGetDatum(conoid));
12093 NULL, 1, &key);
12094 while ((trigtup = systable_getnext(scan)) != NULL)
12095 {
12098
12099 /* Invalid if trigger is not for a referential integrity constraint */
12100 if (!OidIsValid(trgform->tgconstrrelid))
12101 continue;
12102 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12103 continue;
12104 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12105 continue;
12106
12107 /* We should be dropping trigger related to foreign key constraint */
12108 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12109 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12110 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12111 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12112 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12113 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12114 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12115 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12116 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12117 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12118 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12119 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12120
12121 /*
12122 * The constraint is originally set up to contain this trigger as an
12123 * implementation object, so there's a dependency record that links
12124 * the two; however, since the trigger is no longer needed, we remove
12125 * the dependency link in order to be able to drop the trigger while
12126 * keeping the constraint intact.
12127 */
12129 trgform->oid,
12130 false);
12131 /* make dependency deletion visible to performDeletion */
12134 trgform->oid);
12136 /* make trigger drop visible, in case the loop iterates */
12138 }
12139
12140 systable_endscan(scan);
12141}

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

◆ equal_oid_lists()

static bool equal_oid_lists ( const List a,
const List b 
)
static

Definition at line 23029 of file tablecmds.c.

23030{
23031 ListCell *la,
23032 *lb;
23033
23034 if (list_length(a) != list_length(b))
23035 return false;
23036
23037 forboth(la, a, lb, b)
23038 {
23039 if (lfirst_oid(la) != lfirst_oid(lb))
23040 return false;
23041 }
23042 return true;
23043}

References a, b, fb(), forboth, lfirst_oid, and list_length().

Referenced by collectPartitionIndexExtDeps().

◆ evaluateGeneratedExpressionsAndCheckConstraints()

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

Definition at line 22434 of file tablecmds.c.

22438{
22439 econtext->ecxt_scantuple = insertslot;
22440
22442 {
22443 if (!ex->is_generated)
22444 continue;
22445
22446 insertslot->tts_values[ex->attnum - 1]
22447 = ExecEvalExpr(ex->exprstate,
22448 econtext,
22449 &insertslot->tts_isnull[ex->attnum - 1]);
22450 }
22451
22453 {
22454 switch (con->contype)
22455 {
22456 case CONSTR_CHECK:
22457 if (!ExecCheck(con->qualstate, econtext))
22458 ereport(ERROR,
22460 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
22462 errtableconstraint(newPartRel, con->name));
22463 break;
22464 case CONSTR_NOTNULL:
22465 case CONSTR_FOREIGN:
22466 /* Nothing to do here */
22467 break;
22468 default:
22469 elog(ERROR, "unrecognized constraint type: %d",
22470 (int) con->contype);
22471 }
22472 }
22473}
Datum * tts_values
Definition tuptable.h:131

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

1917{
1918 List *rels = NIL;
1919 List *relids = NIL;
1921 ListCell *cell;
1922
1923 /*
1924 * Open, exclusive-lock, and check all the explicitly-specified relations
1925 */
1926 foreach(cell, stmt->relations)
1927 {
1928 RangeVar *rv = lfirst(cell);
1929 Relation rel;
1930 bool recurse = rv->inh;
1931 Oid myrelid;
1932 LOCKMODE lockmode = AccessExclusiveLock;
1933
1934 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1936 NULL);
1937
1938 /* don't throw error for "TRUNCATE foo, foo" */
1939 if (list_member_oid(relids, myrelid))
1940 continue;
1941
1942 /* open the relation, we already hold a lock on it */
1943 rel = table_open(myrelid, NoLock);
1944
1945 /*
1946 * RangeVarGetRelidExtended() has done most checks with its callback,
1947 * but other checks with the now-opened Relation remain.
1948 */
1950
1951 rels = lappend(rels, rel);
1952 relids = lappend_oid(relids, myrelid);
1953
1954 /* Log this relation only if needed for logical decoding */
1957
1958 if (recurse)
1959 {
1960 ListCell *child;
1961 List *children;
1962
1963 children = find_all_inheritors(myrelid, lockmode, NULL);
1964
1965 foreach(child, children)
1966 {
1967 Oid childrelid = lfirst_oid(child);
1968
1969 if (list_member_oid(relids, childrelid))
1970 continue;
1971
1972 /* find_all_inheritors already got lock */
1974
1975 /*
1976 * It is possible that the parent table has children that are
1977 * temp tables of other backends. We cannot safely access
1978 * such tables (because of buffering issues), and the best
1979 * thing to do is to silently ignore them. Note that this
1980 * check is the same as one of the checks done in
1981 * truncate_check_activity() called below, still it is kept
1982 * here for simplicity.
1983 */
1984 if (RELATION_IS_OTHER_TEMP(rel))
1985 {
1986 table_close(rel, lockmode);
1987 continue;
1988 }
1989
1990 /*
1991 * Inherited TRUNCATE commands perform access permission
1992 * checks on the parent table only. So we skip checking the
1993 * children's permissions and don't call
1994 * truncate_check_perms() here.
1995 */
1998
1999 rels = lappend(rels, rel);
2000 relids = lappend_oid(relids, childrelid);
2001
2002 /* Log this relation only if needed for logical decoding */
2005 }
2006 }
2007 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2008 ereport(ERROR,
2010 errmsg("cannot truncate only a partitioned table"),
2011 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
2012 }
2013
2014 ExecuteTruncateGuts(rels, relids, relids_logged,
2015 stmt->behavior, stmt->restart_seqs, false);
2016
2017 /* And close the rels */
2018 foreach(cell, rels)
2019 {
2020 Relation rel = (Relation) lfirst(cell);
2021
2022 table_close(rel, NoLock);
2023 }
2024}
#define RelationIsLogicallyLogged(relation)
Definition rel.h:721
bool inh
Definition primnodes.h:87
static void truncate_check_activity(Relation rel)
Definition tablecmds.c:2496
static void truncate_check_rel(Oid relid, Form_pg_class reltuple)
Definition tablecmds.c:2427
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:2040

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

2045{
2046 List *rels;
2047 List *seq_relids = NIL;
2048 HTAB *ft_htab = NULL;
2049 EState *estate;
2051 ResultRelInfo *resultRelInfo;
2053 ListCell *cell;
2054 Oid *logrelids;
2055
2056 /*
2057 * Check the explicitly-specified relations.
2058 *
2059 * In CASCADE mode, suck in all referencing relations as well. This
2060 * requires multiple iterations to find indirectly-dependent relations. At
2061 * each phase, we need to exclusive-lock new rels before looking for their
2062 * dependencies, else we might miss something. Also, we check each rel as
2063 * soon as we open it, to avoid a faux pas such as holding lock for a long
2064 * time on a rel we have no permissions for.
2065 */
2066 rels = list_copy(explicit_rels);
2067 if (behavior == DROP_CASCADE)
2068 {
2069 for (;;)
2070 {
2071 List *newrelids;
2072
2074 if (newrelids == NIL)
2075 break; /* nothing else to add */
2076
2077 foreach(cell, newrelids)
2078 {
2079 Oid relid = lfirst_oid(cell);
2080 Relation rel;
2081
2082 rel = table_open(relid, AccessExclusiveLock);
2084 (errmsg("truncate cascades to table \"%s\"",
2086 truncate_check_rel(relid, rel->rd_rel);
2087 truncate_check_perms(relid, rel->rd_rel);
2089 rels = lappend(rels, rel);
2090 relids = lappend_oid(relids, relid);
2091
2092 /* Log this relation only if needed for logical decoding */
2095 }
2096 }
2097 }
2098
2099 /*
2100 * Check foreign key references. In CASCADE mode, this should be
2101 * unnecessary since we just pulled in all the references; but as a
2102 * cross-check, do it anyway if in an Assert-enabled build.
2103 */
2104#ifdef USE_ASSERT_CHECKING
2105 heap_truncate_check_FKs(rels, false);
2106#else
2107 if (behavior == DROP_RESTRICT)
2108 heap_truncate_check_FKs(rels, false);
2109#endif
2110
2111 /*
2112 * If we are asked to restart sequences, find all the sequences, lock them
2113 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2114 * We want to do this early since it's pointless to do all the truncation
2115 * work only to fail on sequence permissions.
2116 */
2117 if (restart_seqs)
2118 {
2119 foreach(cell, rels)
2120 {
2121 Relation rel = (Relation) lfirst(cell);
2124
2125 foreach(seqcell, seqlist)
2126 {
2129
2131
2132 /* This check must match AlterSequence! */
2136
2138
2140 }
2141 }
2142 }
2143
2144 /* Prepare to catch AFTER triggers. */
2146
2147 /*
2148 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2149 * each relation. We don't need to call ExecOpenIndices, though.
2150 *
2151 * We put the ResultRelInfos in the es_opened_result_relations list, even
2152 * though we don't have a range table and don't populate the
2153 * es_result_relations array. That's a bit bogus, but it's enough to make
2154 * ExecGetTriggerResultRel() find them.
2155 */
2156 estate = CreateExecutorState();
2158 palloc(list_length(rels) * sizeof(ResultRelInfo));
2159 resultRelInfo = resultRelInfos;
2160 foreach(cell, rels)
2161 {
2162 Relation rel = (Relation) lfirst(cell);
2163
2164 InitResultRelInfo(resultRelInfo,
2165 rel,
2166 0, /* dummy rangetable index */
2167 NULL,
2168 0);
2170 lappend(estate->es_opened_result_relations, resultRelInfo);
2171 resultRelInfo++;
2172 }
2173
2174 /*
2175 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2176 * truncating (this is because one of them might throw an error). Also, if
2177 * we were to allow them to prevent statement execution, that would need
2178 * to be handled here.
2179 */
2180 resultRelInfo = resultRelInfos;
2181 foreach(cell, rels)
2182 {
2184
2186 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2187 &ucxt);
2188 ExecBSTruncateTriggers(estate, resultRelInfo);
2191 resultRelInfo++;
2192 }
2193
2194 /*
2195 * OK, truncate each table.
2196 */
2198
2199 foreach(cell, rels)
2200 {
2201 Relation rel = (Relation) lfirst(cell);
2202
2203 /* Skip partitioned tables as there is nothing to do */
2204 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2205 continue;
2206
2207 /*
2208 * Build the lists of foreign tables belonging to each foreign server
2209 * and pass each list to the foreign data wrapper's callback function,
2210 * so that each server can truncate its all foreign tables in bulk.
2211 * Each list is saved as a single entry in a hash table that uses the
2212 * server OID as lookup key.
2213 */
2214 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2215 {
2217 bool found;
2219
2220 /* First time through, initialize hashtable for foreign tables */
2221 if (!ft_htab)
2222 {
2223 HASHCTL hctl;
2224
2225 memset(&hctl, 0, sizeof(HASHCTL));
2226 hctl.keysize = sizeof(Oid);
2227 hctl.entrysize = sizeof(ForeignTruncateInfo);
2229
2230 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2231 32, /* start small and extend */
2232 &hctl,
2234 }
2235
2236 /* Find or create cached entry for the foreign table */
2237 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2238 if (!found)
2239 ft_info->rels = NIL;
2240
2241 /*
2242 * Save the foreign table in the entry of the server that the
2243 * foreign table belongs to.
2244 */
2245 ft_info->rels = lappend(ft_info->rels, rel);
2246 continue;
2247 }
2248
2249 /*
2250 * Normally, we need a transaction-safe truncation here. However, if
2251 * the table was either created in the current (sub)transaction or has
2252 * a new relfilenumber in the current (sub)transaction, then we can
2253 * just truncate it in-place, because a rollback would cause the whole
2254 * table or the current physical file to be thrown away anyway.
2255 */
2256 if (rel->rd_createSubid == mySubid ||
2258 {
2259 /* Immediate, non-rollbackable truncation is OK */
2261 }
2262 else
2263 {
2267
2268 /*
2269 * This effectively deletes all rows in the table, and may be done
2270 * in a serializable transaction. In that case we must record a
2271 * rw-conflict in to this transaction from each transaction
2272 * holding a predicate lock on the table.
2273 */
2275
2276 /*
2277 * Need the full transaction-safe pushups.
2278 *
2279 * Create a new empty storage file for the relation, and assign it
2280 * as the relfilenumber value. The old storage file is scheduled
2281 * for deletion at commit.
2282 */
2283 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2284
2286
2287 /*
2288 * The same for the toast table, if any.
2289 */
2290 toast_relid = rel->rd_rel->reltoastrelid;
2292 {
2295
2297 toastrel->rd_rel->relpersistence);
2299 }
2300
2301 /*
2302 * Reconstruct the indexes to match, and we're done.
2303 */
2306 }
2307
2309 }
2310
2311 /* Now go through the hash table, and truncate foreign tables */
2312 if (ft_htab)
2313 {
2316
2318
2319 PG_TRY();
2320 {
2321 while ((ft_info = hash_seq_search(&seq)) != NULL)
2322 {
2323 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2324
2325 /* truncate_check_rel() has checked that already */
2326 Assert(routine->ExecForeignTruncate != NULL);
2327
2328 routine->ExecForeignTruncate(ft_info->rels,
2329 behavior,
2330 restart_seqs);
2331 }
2332 }
2333 PG_FINALLY();
2334 {
2336 }
2337 PG_END_TRY();
2338 }
2339
2340 /*
2341 * Restart owned sequences if we were asked to.
2342 */
2343 foreach(cell, seq_relids)
2344 {
2345 Oid seq_relid = lfirst_oid(cell);
2346
2348 }
2349
2350 /*
2351 * Write a WAL record to allow this set of actions to be logically
2352 * decoded.
2353 *
2354 * Assemble an array of relids so we can write a single WAL record for the
2355 * whole action.
2356 */
2357 if (relids_logged != NIL)
2358 {
2360 int i = 0;
2361
2362 /* should only get here if effective_wal_level is 'logical' */
2364
2366 foreach(cell, relids_logged)
2367 logrelids[i++] = lfirst_oid(cell);
2368
2369 xlrec.dbId = MyDatabaseId;
2370 xlrec.nrelids = list_length(relids_logged);
2371 xlrec.flags = 0;
2372 if (behavior == DROP_CASCADE)
2373 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2374 if (restart_seqs)
2376
2380
2382
2384 }
2385
2386 /*
2387 * Process all AFTER STATEMENT TRUNCATE triggers.
2388 */
2389 resultRelInfo = resultRelInfos;
2390 foreach(cell, rels)
2391 {
2393
2395 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2396 &ucxt);
2397 ExecASTruncateTriggers(estate, resultRelInfo);
2400 resultRelInfo++;
2401 }
2402
2403 /* Handle queued AFTER triggers */
2404 AfterTriggerEndQuery(estate);
2405
2406 /* We can clean up the EState now */
2407 FreeExecutorState(estate);
2408
2409 /*
2410 * Close any rels opened by CASCADE (can't do this while EState still
2411 * holds refs)
2412 */
2413 rels = list_difference_ptr(rels, explicit_rels);
2414 foreach(cell, rels)
2415 {
2416 Relation rel = (Relation) lfirst(cell);
2417
2418 table_close(rel, NoLock);
2419 }
2420}
uint32 SubTransactionId
Definition c.h:740
void ResetSequence(Oid seq_relid)
Definition sequence.c:255
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition dynahash.c:889
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition dynahash.c:360
void hash_destroy(HTAB *hashp)
Definition dynahash.c:802
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition dynahash.c:1352
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition dynahash.c:1317
#define PG_TRY(...)
Definition elog.h:374
#define PG_END_TRY(...)
Definition elog.h:399
#define PG_FINALLY(...)
Definition elog.h:391
FdwRoutine * GetFdwRoutineByServerId(Oid serverid)
Definition foreign.c:409
Oid GetForeignServerIdByRelId(Oid relid)
Definition foreign.c:387
List * heap_truncate_find_FKs(List *relationIds)
Definition heap.c:3791
void heap_truncate_check_FKs(List *relations, bool tempTables)
Definition heap.c:3696
void heap_truncate_one_rel(Relation rel)
Definition heap.c:3652
#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:109
#define HASH_CONTEXT
Definition hsearch.h:97
#define HASH_ELEM
Definition hsearch.h:90
#define HASH_BLOBS
Definition hsearch.h:92
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition index.c:3969
#define REINDEX_REL_PROCESS_TOAST
Definition index.h:166
List * list_difference_ptr(const List *list1, const List *list2)
Definition list.c:1263
@ DROP_CASCADE
@ OBJECT_SEQUENCE
void pgstat_count_truncate(Relation rel)
void CheckTableForSerializableConflictIn(Relation relation)
Definition predicate.c:4348
void RelationSetNewRelfilenumber(Relation relation, char persistence)
Definition relcache.c:3775
List * es_opened_result_relations
Definition execnodes.h:725
ExecForeignTruncate_function ExecForeignTruncate
Definition fdwapi.h:268
Size keysize
Definition hsearch.h:69
Size entrysize
Definition hsearch.h:70
MemoryContext hcxt
Definition hsearch.h:81
SubTransactionId rd_newRelfilelocatorSubid
Definition rel.h:104
Relation ri_RelationDesc
Definition execnodes.h:514
static void truncate_check_perms(Oid relid, Form_pg_class reltuple)
Definition tablecmds.c:2478
void ExecBSTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:3282
void ExecASTruncateTriggers(EState *estate, ResultRelInfo *relinfo)
Definition trigger.c:3329
void AfterTriggerEndQuery(EState *estate)
Definition trigger.c:5161
void AfterTriggerBeginQuery(void)
Definition trigger.c:5141
void SwitchToUntrustedUser(Oid userid, UserContext *context)
Definition usercontext.c:33
void RestoreUserContext(UserContext *context)
Definition usercontext.c:87
SubTransactionId GetCurrentSubTransactionId(void)
Definition xact.c:793
#define XLogLogicalInfoActive()
Definition xlog.h:137
#define XLOG_INCLUDE_ORIGIN
Definition xlog.h:166
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition xloginsert.c:482
void XLogRegisterData(const void *data, uint32 len)
Definition xloginsert.c:372
void XLogSetRecordFlags(uint8 flags)
Definition xloginsert.c:464
void XLogBeginInsert(void)
Definition xloginsert.c:153

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

7028{
7030 ScanKeyData key[2];
7033
7034 /* since this function recurses, it could be driven to stack overflow */
7036
7037 /*
7038 * We scan pg_depend to find those things that depend on the given type.
7039 * (We assume we can ignore refobjsubid for a type.)
7040 */
7042
7043 ScanKeyInit(&key[0],
7047 ScanKeyInit(&key[1],
7050 ObjectIdGetDatum(typeOid));
7051
7053 NULL, 2, key);
7054
7056 {
7058 Relation rel;
7061
7062 /* Check for directly dependent types */
7063 if (pg_depend->classid == TypeRelationId)
7064 {
7065 /*
7066 * This must be an array, domain, or range containing the given
7067 * type, so recursively check for uses of this type. Note that
7068 * any error message will mention the original type not the
7069 * container; this is intentional.
7070 */
7073 continue;
7074 }
7075
7076 /* Else, ignore dependees that aren't relations */
7077 if (pg_depend->classid != RelationRelationId)
7078 continue;
7079
7082
7083 /*
7084 * If objsubid identifies a specific column, refer to that in error
7085 * messages. Otherwise, search to see if there's a user column of the
7086 * type. (We assume system columns are never of interesting types.)
7087 * The search is needed because an index containing an expression
7088 * column of the target type will just be recorded as a whole-relation
7089 * dependency. If we do not find a column of the type, the dependency
7090 * must indicate that the type is transiently referenced in an index
7091 * expression but not stored on disk, which we assume is OK, just as
7092 * we do for references in views. (It could also be that the target
7093 * type is embedded in some container type that is stored in an index
7094 * column, but the previous recursion should catch such cases.)
7095 */
7096 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
7097 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
7098 else
7099 {
7100 att = NULL;
7101 for (int attno = 1; attno <= tupleDesc->natts; attno++)
7102 {
7103 att = TupleDescAttr(tupleDesc, attno - 1);
7104 if (att->atttypid == typeOid && !att->attisdropped)
7105 break;
7106 att = NULL;
7107 }
7108 if (att == NULL)
7109 {
7110 /* No such column, so assume OK */
7112 continue;
7113 }
7114 }
7115
7116 /*
7117 * We definitely should reject if the relation has storage. If it's
7118 * partitioned, then perhaps we don't have to reject: if there are
7119 * partitions then we'll fail when we find one, else there is no
7120 * stored data to worry about. However, it's possible that the type
7121 * change would affect conclusions about whether the type is sortable
7122 * or hashable and thus (if it's a partitioning column) break the
7123 * partitioning rule. For now, reject for partitioned rels too.
7124 */
7125 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7126 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7127 {
7128 if (origTypeName)
7129 ereport(ERROR,
7131 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7134 NameStr(att->attname))));
7135 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7136 ereport(ERROR,
7138 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7141 NameStr(att->attname))));
7142 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7143 ereport(ERROR,
7145 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7148 NameStr(att->attname))));
7149 else
7150 ereport(ERROR,
7152 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7155 NameStr(att->attname))));
7156 }
7157 else if (OidIsValid(rel->rd_rel->reltype))
7158 {
7159 /*
7160 * A view or composite type itself isn't a problem, but we must
7161 * recursively check for indirect dependencies via its rowtype.
7162 */
7165 }
7166
7168 }
7169
7171
7173}

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

7185{
7187 ScanKeyData key[1];
7188 TableScanDesc scan;
7189 HeapTuple tuple;
7190 List *result = NIL;
7191
7193
7194 ScanKeyInit(&key[0],
7197 ObjectIdGetDatum(typeOid));
7198
7199 scan = table_beginscan_catalog(classRel, 1, key);
7200
7201 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7202 {
7204
7205 if (behavior == DROP_RESTRICT)
7206 ereport(ERROR,
7208 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7209 typeName),
7210 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7211 else
7213 }
7214
7215 table_endscan(scan);
7217
7218 return result;
7219}

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

3668{
3669 ListCell *lc;
3670 int i = 1;
3671
3672 foreach(lc, columns)
3673 {
3674 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3675 return i;
3676
3677 i++;
3678 }
3679 return 0;
3680}

References fb(), i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 13892 of file tablecmds.c.

13893{
13894 CoercionPathType ret;
13895
13897 {
13899 *funcid = InvalidOid;
13900 }
13901 else
13902 {
13904 COERCION_IMPLICIT, funcid);
13905 if (ret == COERCION_PATH_NONE)
13906 /* A previously-relied-upon cast is now gone. */
13907 elog(ERROR, "could not find cast from %u to %u",
13909 }
13910
13911 return ret;
13912}
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().

◆ freePartitionIndexExtDeps()

static void freePartitionIndexExtDeps ( List extDepState)
static

Definition at line 23232 of file tablecmds.c.

23233{
23235 {
23236 list_free(entry->extensionOids);
23237 pfree(entry);
23238 }
23240}

References fb(), foreach_ptr, list_free(), and pfree().

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ GetAttributeCompression()

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

Definition at line 22318 of file tablecmds.c.

22319{
22320 char cmethod;
22321
22322 if (compression == NULL || strcmp(compression, "default") == 0)
22324
22325 /*
22326 * To specify a nondefault method, the column data type must be toastable.
22327 * Note this says nothing about whether the column's attstorage setting
22328 * permits compression; we intentionally allow attstorage and
22329 * attcompression to be independent. But with a non-toastable type,
22330 * attstorage could not be set to a value that would permit compression.
22331 *
22332 * We don't actually need to enforce this, since nothing bad would happen
22333 * if attcompression were non-default; it would never be consulted. But
22334 * it seems more user-friendly to complain about a certainly-useless
22335 * attempt to set the property.
22336 */
22338 ereport(ERROR,
22340 errmsg("column data type %s does not support compression",
22342
22343 cmethod = CompressionNameToMethod(compression);
22345 ereport(ERROR,
22347 errmsg("invalid compression method \"%s\"", compression)));
22348
22349 return cmethod;
22350}
#define TypeIsToastable(typid)
Definition lsyscache.h:227
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 22479 of file tablecmds.c.

22480{
22483 List *colList = NIL;
22484
22486
22487 for (parent_attno = 1; parent_attno <= modelDesc->natts;
22488 parent_attno++)
22489 {
22491 parent_attno - 1);
22492 ColumnDef *def;
22493
22494 /* Ignore dropped columns in the parent. */
22495 if (attribute->attisdropped)
22496 continue;
22497
22498 def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
22499 attribute->atttypmod, attribute->attcollation);
22500
22501 def->is_not_null = attribute->attnotnull;
22502
22503 /* Copy identity. */
22504 def->identity = attribute->attidentity;
22505
22506 /* Copy attgenerated. */
22507 def->generated = attribute->attgenerated;
22508
22509 def->storage = attribute->attstorage;
22510
22511 /* Likewise, copy compression. */
22512 if (CompressionMethodIsValid(attribute->attcompression))
22513 def->compression =
22514 pstrdup(GetCompressionMethodName(attribute->attcompression));
22515 else
22516 def->compression = NULL;
22517
22518 /* Add to column list. */
22519 colList = lappend(colList, def);
22520 }
22521
22522 return colList;
22523}
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 22356 of file tablecmds.c.

22357{
22358 char cstorage = 0;
22359
22360 if (pg_strcasecmp(storagemode, "plain") == 0)
22362 else if (pg_strcasecmp(storagemode, "external") == 0)
22364 else if (pg_strcasecmp(storagemode, "extended") == 0)
22366 else if (pg_strcasecmp(storagemode, "main") == 0)
22368 else if (pg_strcasecmp(storagemode, "default") == 0)
22370 else
22371 ereport(ERROR,
22373 errmsg("invalid storage type \"%s\"",
22374 storagemode)));
22375
22376 /*
22377 * safety check: do not allow toasted storage modes unless column datatype
22378 * is TOAST-aware.
22379 */
22381 ereport(ERROR,
22383 errmsg("column data type %s can only have storage PLAIN",
22385
22386 return cstorage;
22387}
char get_typstorage(Oid typid)
Definition lsyscache.c:2727
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 12149 of file tablecmds.c.

12153{
12155 SysScanDesc scan;
12157
12159 ScanKeyInit(&key,
12162 ObjectIdGetDatum(conoid));
12163
12165 NULL, 1, &key);
12166 while ((trigtup = systable_getnext(scan)) != NULL)
12167 {
12169
12170 if (trgform->tgconstrrelid != conrelid)
12171 continue;
12172 if (trgform->tgrelid != confrelid)
12173 continue;
12174 /* Only ever look at "action" triggers on the PK side. */
12176 continue;
12177 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12178 {
12180 *deleteTriggerOid = trgform->oid;
12181 }
12182 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12183 {
12185 *updateTriggerOid = trgform->oid;
12186 }
12187#ifndef USE_ASSERT_CHECKING
12188 /* In an assert-enabled build, continue looking to find duplicates */
12190 break;
12191#endif
12192 }
12193
12195 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12196 conoid);
12198 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12199 conoid);
12200
12201 systable_endscan(scan);
12202}
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 12210 of file tablecmds.c.

12214{
12216 SysScanDesc scan;
12218
12220 ScanKeyInit(&key,
12223 ObjectIdGetDatum(conoid));
12224
12226 NULL, 1, &key);
12227 while ((trigtup = systable_getnext(scan)) != NULL)
12228 {
12230
12231 if (trgform->tgconstrrelid != confrelid)
12232 continue;
12233 if (trgform->tgrelid != conrelid)
12234 continue;
12235 /* Only ever look at "check" triggers on the FK side. */
12237 continue;
12238 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12239 {
12241 *insertTriggerOid = trgform->oid;
12242 }
12243 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12244 {
12246 *updateTriggerOid = trgform->oid;
12247 }
12248#ifndef USE_ASSERT_CHECKING
12249 /* In an assert-enabled build, continue looking to find duplicates. */
12251 break;
12252#endif
12253 }
12254
12256 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12257 conoid);
12259 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12260 conoid);
12261
12262 systable_endscan(scan);
12263}
#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 22216 of file tablecmds.c.

22217{
22219 HeapTuple tuple;
22220 SysScanDesc scan;
22221 ScanKeyData key[2];
22222 List *constraints = NIL;
22223
22224 /*
22225 * If no indexes, or no columns are referenceable by FKs, we can avoid the
22226 * scan.
22227 */
22231 return NIL;
22232
22233 /* Search for constraints referencing this table */
22235 ScanKeyInit(&key[0],
22238 ScanKeyInit(&key[1],
22241
22242 /* XXX This is a seqscan, as we don't have a usable index */
22243 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
22244 while ((tuple = systable_getnext(scan)) != NULL)
22245 {
22247
22248 /*
22249 * We only need to process constraints that are part of larger ones.
22250 */
22251 if (!OidIsValid(constrForm->conparentid))
22252 continue;
22253
22254 constraints = lappend_oid(constraints, constrForm->oid);
22255 }
22256
22257 systable_endscan(scan);
22259
22260 return constraints;
22261}
#define bms_is_empty(a)
Definition bitmapset.h:118
@ INDEX_ATTR_BITMAP_KEY
Definition relcache.h:70

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

17405{
17407
17408 /*
17409 * Since we copy the file directly without looking at the shared buffers,
17410 * we'd better first flush out any pages of the source relation that are
17411 * in shared buffers. We assume no new changes will be made while we are
17412 * holding exclusive lock on the rel.
17413 */
17415
17416 /*
17417 * Create and copy all forks of the relation, and schedule unlinking of
17418 * old physical files.
17419 *
17420 * NOTE: any conflict in relfilenumber value will be caught in
17421 * RelationCreateStorage().
17422 */
17423 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17424
17425 /* copy main fork */
17427 rel->rd_rel->relpersistence);
17428
17429 /* copy those extra forks that exist */
17430 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17431 forkNum <= MAX_FORKNUM; forkNum++)
17432 {
17433 if (smgrexists(RelationGetSmgr(rel), forkNum))
17434 {
17435 smgrcreate(dstrel, forkNum, false);
17436
17437 /*
17438 * WAL log creation if the relation is persistent, or this is the
17439 * init fork of an unlogged relation.
17440 */
17441 if (RelationIsPermanent(rel) ||
17442 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17443 forkNum == INIT_FORKNUM))
17444 log_smgrcreate(&newrlocator, forkNum);
17446 rel->rd_rel->relpersistence);
17447 }
17448 }
17449
17450 /* drop old relation, and close new one */
17453}
void FlushRelationBuffers(Relation rel)
Definition bufmgr.c:5171
static SMgrRelation RelationGetSmgr(Relation rel)
Definition rel.h:578
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 18115 of file tablecmds.c.

18116{
18118 SysScanDesc scan;
18121 bool found = false;
18122
18123 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
18124
18125 /*
18126 * Find pg_inherits entries by inhparent. (We need to scan them all in
18127 * order to verify that no other partition is pending detach.)
18128 */
18130 ScanKeyInit(&key,
18135 true, NULL, 1, &key);
18136
18138 {
18140
18142 if (inhForm->inhdetachpending)
18143 ereport(ERROR,
18145 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
18146 get_rel_name(inhForm->inhrelid),
18147 get_namespace_name(parent_rel->rd_rel->relnamespace),
18149 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
18150
18151 if (inhForm->inhrelid == RelationGetRelid(child_rel))
18152 {
18154
18157
18159 &inheritsTuple->t_self,
18160 newtup);
18161 found = true;
18163 /* keep looking, to ensure we catch others pending detach */
18164 }
18165 }
18166
18167 /* Done */
18168 systable_endscan(scan);
18170
18171 if (!found)
18172 ereport(ERROR,
18174 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18177}
#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 2604 of file tablecmds.c.

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

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

17754{
17757
17760
17762 {
17764 char *parent_attname = NameStr(parent_att->attname);
17765 HeapTuple tuple;
17766
17767 /* Ignore dropped columns in the parent. */
17768 if (parent_att->attisdropped)
17769 continue;
17770
17771 /* Find same column in child (matching on column name). */
17773 if (HeapTupleIsValid(tuple))
17774 {
17776
17777 if (parent_att->atttypid != child_att->atttypid ||
17778 parent_att->atttypmod != child_att->atttypmod)
17779 ereport(ERROR,
17781 errmsg("child table \"%s\" has different type for column \"%s\"",
17783
17784 if (parent_att->attcollation != child_att->attcollation)
17785 ereport(ERROR,
17787 errmsg("child table \"%s\" has different collation for column \"%s\"",
17789
17790 /*
17791 * If the parent has a not-null constraint that's not NO INHERIT,
17792 * make sure the child has one too.
17793 *
17794 * Other constraints are checked elsewhere.
17795 */
17796 if (parent_att->attnotnull && !child_att->attnotnull)
17797 {
17799
17801 parent_att->attnum);
17802 if (HeapTupleIsValid(contup) &&
17804 ereport(ERROR,
17806 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
17808 }
17809
17810 /*
17811 * Child column must be generated if and only if parent column is.
17812 */
17813 if (parent_att->attgenerated && !child_att->attgenerated)
17814 ereport(ERROR,
17816 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
17817 if (child_att->attgenerated && !parent_att->attgenerated)
17818 ereport(ERROR,
17820 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
17821
17822 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
17823 ereport(ERROR,
17825 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
17826 errdetail("Parent column is %s, child column is %s.",
17827 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
17828 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
17829
17830 /*
17831 * Regular inheritance children are independent enough not to
17832 * inherit identity columns. But partitions are integral part of
17833 * a partitioned table and inherit identity column.
17834 */
17835 if (ispartition)
17836 child_att->attidentity = parent_att->attidentity;
17837
17838 /*
17839 * OK, bump the child column's inheritance count. (If we fail
17840 * later on, this change will just roll back.)
17841 */
17842 if (pg_add_s16_overflow(child_att->attinhcount, 1,
17843 &child_att->attinhcount))
17844 ereport(ERROR,
17846 errmsg("too many inheritance parents"));
17847
17848 /*
17849 * In case of partitions, we must enforce that value of attislocal
17850 * is same in all partitions. (Note: there are only inherited
17851 * attributes in partitions)
17852 */
17853 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17854 {
17855 Assert(child_att->attinhcount == 1);
17856 child_att->attislocal = false;
17857 }
17858
17859 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
17860 heap_freetuple(tuple);
17861 }
17862 else
17863 {
17864 ereport(ERROR,
17866 errmsg("child table is missing column \"%s\"", parent_attname)));
17867 }
17868 }
17869
17871}

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

3225{
3226 ListCell *lc;
3228
3229 foreach(lc, constraints)
3230 {
3232
3233 Assert(ccon->contype == CONSTR_CHECK);
3234
3235 /* Non-matching names never conflict */
3236 if (strcmp(ccon->name, name) != 0)
3237 continue;
3238
3239 if (equal(expr, ccon->expr))
3240 {
3241 /* OK to merge constraint with existing */
3242 if (pg_add_s16_overflow(ccon->inhcount, 1,
3243 &ccon->inhcount))
3244 ereport(ERROR,
3246 errmsg("too many inheritance parents"));
3247
3248 /*
3249 * When enforceability differs, the merged constraint should be
3250 * marked as ENFORCED because one of the parents is ENFORCED.
3251 */
3252 if (!ccon->is_enforced && is_enforced)
3253 {
3254 ccon->is_enforced = true;
3255 ccon->skip_validation = false;
3256 }
3257
3258 return constraints;
3259 }
3260
3261 ereport(ERROR,
3263 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3264 name)));
3265 }
3266
3267 /*
3268 * Constraint couldn't be merged with an existing one and also didn't
3269 * conflict with an existing one, so add it as a new one to the list.
3270 */
3272 newcon->contype = CONSTR_CHECK;
3273 newcon->name = pstrdup(name);
3274 newcon->expr = expr;
3275 newcon->inhcount = 1;
3276 newcon->is_enforced = is_enforced;
3277 newcon->skip_validation = !is_enforced;
3278 return lappend(constraints, newcon);
3279}

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

3304{
3305 char *attributeName = newdef->colname;
3307 Oid inhtypeid,
3308 newtypeid;
3310 newtypmod;
3311 Oid inhcollid,
3312 newcollid;
3313
3316 (errmsg("merging column \"%s\" with inherited definition",
3317 attributeName)));
3318 else
3320 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3321 errdetail("User-specified column moved to the position of the inherited column.")));
3322
3324
3325 /*
3326 * Must have the same type and typmod
3327 */
3331 ereport(ERROR,
3333 errmsg("column \"%s\" has a type conflict",
3335 errdetail("%s versus %s",
3338
3339 /*
3340 * Must have the same collation
3341 */
3344 if (inhcollid != newcollid)
3345 ereport(ERROR,
3347 errmsg("column \"%s\" has a collation conflict",
3349 errdetail("\"%s\" versus \"%s\"",
3352
3353 /*
3354 * Identity is never inherited by a regular inheritance child. Pick
3355 * child's identity definition if there's one.
3356 */
3357 inhdef->identity = newdef->identity;
3358
3359 /*
3360 * Copy storage parameter
3361 */
3362 if (inhdef->storage == 0)
3363 inhdef->storage = newdef->storage;
3364 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3365 ereport(ERROR,
3367 errmsg("column \"%s\" has a storage parameter conflict",
3369 errdetail("%s versus %s",
3370 storage_name(inhdef->storage),
3371 storage_name(newdef->storage))));
3372
3373 /*
3374 * Copy compression parameter
3375 */
3376 if (inhdef->compression == NULL)
3377 inhdef->compression = newdef->compression;
3378 else if (newdef->compression != NULL)
3379 {
3380 if (strcmp(inhdef->compression, newdef->compression) != 0)
3381 ereport(ERROR,
3383 errmsg("column \"%s\" has a compression method conflict",
3385 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3386 }
3387
3388 /*
3389 * Merge of not-null constraints = OR 'em together
3390 */
3391 inhdef->is_not_null |= newdef->is_not_null;
3392
3393 /*
3394 * Check for conflicts related to generated columns.
3395 *
3396 * If the parent column is generated, the child column will be made a
3397 * generated column if it isn't already. If it is a generated column,
3398 * we'll take its generation expression in preference to the parent's. We
3399 * must check that the child column doesn't specify a default value or
3400 * identity, which matches the rules for a single column in
3401 * parse_utilcmd.c.
3402 *
3403 * Conversely, if the parent column is not generated, the child column
3404 * can't be either. (We used to allow that, but it results in being able
3405 * to override the generation expression via UPDATEs through the parent.)
3406 */
3407 if (inhdef->generated)
3408 {
3409 if (newdef->raw_default && !newdef->generated)
3410 ereport(ERROR,
3412 errmsg("column \"%s\" inherits from generated column but specifies default",
3413 inhdef->colname)));
3414 if (newdef->identity)
3415 ereport(ERROR,
3417 errmsg("column \"%s\" inherits from generated column but specifies identity",
3418 inhdef->colname)));
3419 }
3420 else
3421 {
3422 if (newdef->generated)
3423 ereport(ERROR,
3425 errmsg("child column \"%s\" specifies generation expression",
3426 inhdef->colname),
3427 errhint("A child table column cannot be generated unless its parent column is.")));
3428 }
3429
3430 if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3431 ereport(ERROR,
3433 errmsg("column \"%s\" inherits from generated column of different kind",
3434 inhdef->colname),
3435 errdetail("Parent column is %s, child column is %s.",
3436 inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3437 newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3438
3439 /*
3440 * If new def has a default, override previous default
3441 */
3442 if (newdef->raw_default != NULL)
3443 {
3444 inhdef->raw_default = newdef->raw_default;
3445 inhdef->cooked_default = newdef->cooked_default;
3446 }
3447
3448 /* Mark the column as locally defined */
3449 inhdef->is_local = true;
3450}
static const char * storage_name(char c)
Definition tablecmds.c:2519

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

17892{
17897 Oid parent_relid = RelationGetRelid(parent_rel);
17898 AttrMap *attmap;
17899
17901
17902 /* Outer loop scans through the parent's constraint definitions */
17906 ObjectIdGetDatum(parent_relid));
17908 true, NULL, 1, &parent_key);
17909
17912 true);
17913
17915 {
17921 bool found = false;
17922
17923 if (parent_con->contype != CONSTRAINT_CHECK &&
17924 parent_con->contype != CONSTRAINT_NOTNULL)
17925 continue;
17926
17927 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17928 if (parent_con->connoinherit)
17929 continue;
17930
17931 if (parent_con->contype == CONSTRAINT_NOTNULL)
17933 else
17935
17936 /* Search for a child constraint matching this one */
17942 true, NULL, 1, &child_key);
17943
17945 {
17948
17949 if (child_con->contype != parent_con->contype)
17950 continue;
17951
17952 /*
17953 * CHECK constraint are matched by constraint name, NOT NULL ones
17954 * by attribute number.
17955 */
17956 if (child_con->contype == CONSTRAINT_CHECK)
17957 {
17958 if (strcmp(NameStr(parent_con->conname),
17959 NameStr(child_con->conname)) != 0)
17960 continue;
17961 }
17962 else if (child_con->contype == CONSTRAINT_NOTNULL)
17963 {
17967
17970 if (parent_attno != attmap->attnums[child_attno - 1])
17971 continue;
17972
17974 /* there shouldn't be constraints on dropped columns */
17975 if (parent_attr->attisdropped || child_attr->attisdropped)
17976 elog(ERROR, "found not-null constraint on dropped columns");
17977 }
17978
17979 if (child_con->contype == CONSTRAINT_CHECK &&
17981 ereport(ERROR,
17983 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17985
17986 /*
17987 * If the child constraint is "no inherit" then cannot merge
17988 */
17989 if (child_con->connoinherit)
17990 ereport(ERROR,
17992 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17994
17995 /*
17996 * If the child constraint is "not valid" then cannot merge with a
17997 * valid parent constraint
17998 */
17999 if (parent_con->convalidated && child_con->conenforced &&
18000 !child_con->convalidated)
18001 ereport(ERROR,
18003 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
18005
18006 /*
18007 * A NOT ENFORCED child constraint cannot be merged with an
18008 * ENFORCED parent constraint. However, the reverse is allowed,
18009 * where the child constraint is ENFORCED.
18010 */
18011 if (parent_con->conenforced && !child_con->conenforced)
18012 ereport(ERROR,
18014 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
18016
18017 /*
18018 * OK, bump the child constraint's inheritance count. (If we fail
18019 * later on, this change will just roll back.)
18020 */
18023
18024 if (pg_add_s16_overflow(child_con->coninhcount, 1,
18025 &child_con->coninhcount))
18026 ereport(ERROR,
18028 errmsg("too many inheritance parents"));
18029
18030 /*
18031 * In case of partitions, an inherited constraint must be
18032 * inherited only once since it cannot have multiple parents and
18033 * it is never considered local.
18034 */
18035 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18036 {
18037 Assert(child_con->coninhcount == 1);
18038 child_con->conislocal = false;
18039 }
18040
18043
18044 found = true;
18045 break;
18046 }
18047
18049
18050 if (!found)
18051 {
18052 if (parent_con->contype == CONSTRAINT_NOTNULL)
18053 ereport(ERROR,
18055 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
18056 get_attname(parent_relid,
18058 false),
18060
18061 ereport(ERROR,
18063 errmsg("child table is missing constraint \"%s\"",
18064 NameStr(parent_con->conname))));
18065 }
18066 }
18067
18070}
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 3473 of file tablecmds.c.

3476{
3477 char *attributeName = newdef->colname;
3480 newtypeid;
3482 newtypmod;
3484 newcollid;
3485
3487 (errmsg("merging multiple inherited definitions of column \"%s\"",
3488 attributeName)));
3490
3491 /*
3492 * Must have the same type and typmod
3493 */
3497 ereport(ERROR,
3499 errmsg("inherited column \"%s\" has a type conflict",
3501 errdetail("%s versus %s",
3504
3505 /*
3506 * Must have the same collation
3507 */
3510 if (prevcollid != newcollid)
3511 ereport(ERROR,
3513 errmsg("inherited column \"%s\" has a collation conflict",
3515 errdetail("\"%s\" versus \"%s\"",
3518
3519 /*
3520 * Copy/check storage parameter
3521 */
3522 if (prevdef->storage == 0)
3523 prevdef->storage = newdef->storage;
3524 else if (prevdef->storage != newdef->storage)
3525 ereport(ERROR,
3527 errmsg("inherited column \"%s\" has a storage parameter conflict",
3529 errdetail("%s versus %s",
3530 storage_name(prevdef->storage),
3531 storage_name(newdef->storage))));
3532
3533 /*
3534 * Copy/check compression parameter
3535 */
3536 if (prevdef->compression == NULL)
3537 prevdef->compression = newdef->compression;
3538 else if (newdef->compression != NULL)
3539 {
3540 if (strcmp(prevdef->compression, newdef->compression) != 0)
3541 ereport(ERROR,
3543 errmsg("column \"%s\" has a compression method conflict",
3545 errdetail("%s versus %s",
3546 prevdef->compression, newdef->compression)));
3547 }
3548
3549 /*
3550 * Check for GENERATED conflicts
3551 */
3552 if (prevdef->generated != newdef->generated)
3553 ereport(ERROR,
3555 errmsg("inherited column \"%s\" has a generation conflict",
3556 attributeName)));
3557
3558 /*
3559 * Default and other constraints are handled by the caller.
3560 */
3561
3562 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3563 &prevdef->inhcount))
3564 ereport(ERROR,
3566 errmsg("too many inheritance parents"));
3567
3568 return prevdef;
3569}

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

22853{
22854 CommandId mycid;
22855 EState *estate;
22856 AlteredTableInfo *tab;
22857 ListCell *ltab;
22858
22859 /* The FSM is empty, so don't bother using it. */
22860 uint32 ti_options = TABLE_INSERT_SKIP_FSM;
22861 BulkInsertState bistate; /* state of bulk inserts for partition */
22862 TupleTableSlot *dstslot;
22863
22864 /* Find the work queue entry for the new partition table: newPartRel. */
22866
22867 /* Generate the constraint and default execution states. */
22868 estate = CreateExecutorState();
22869
22871
22872 mycid = GetCurrentCommandId(true);
22873
22874 /* Prepare a BulkInsertState for table_tuple_insert. */
22875 bistate = GetBulkInsertState();
22876
22877 /* Create the necessary tuple slot. */
22878 dstslot = table_slot_create(newPartRel, NULL);
22879
22881 {
22882 ExprContext *econtext;
22885 TableScanDesc scan;
22887 Snapshot snapshot;
22889
22890 econtext = GetPerTupleExprContext(estate);
22891
22892 /*
22893 * Partition is already locked in the transformPartitionCmdForMerge
22894 * function.
22895 */
22897
22898 /* Create a source tuple slot for the partition being merged. */
22900
22901 /*
22902 * Map computing for moving attributes of the merged partition to the
22903 * new partition.
22904 */
22907
22908 /* Scan through the rows. */
22909 snapshot = RegisterSnapshot(GetLatestSnapshot());
22910 scan = table_beginscan(mergingPartition, snapshot, 0, NULL,
22911 SO_NONE);
22912
22913 /*
22914 * Switch to per-tuple memory context and reset it for each tuple
22915 * produced, so we don't leak memory.
22916 */
22918
22920 {
22922
22924
22925 if (tuple_map)
22926 {
22927 /* Need to use a map to copy attributes. */
22928 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
22929 }
22930 else
22931 {
22933
22934 /* Copy attributes directly. */
22935 insertslot = dstslot;
22936
22938
22939 memcpy(insertslot->tts_values, srcslot->tts_values,
22940 sizeof(Datum) * srcslot->tts_nvalid);
22941 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
22942 sizeof(bool) * srcslot->tts_nvalid);
22943
22945 }
22946
22947 /*
22948 * Constraints and GENERATED expressions might reference the
22949 * tableoid column, so fill tts_tableOid with the desired value.
22950 * (We must do this each time, because it gets overwritten with
22951 * newrel's OID during storing.)
22952 */
22953 insertslot->tts_tableOid = RelationGetRelid(newPartRel);
22954
22955 /*
22956 * Now, evaluate any generated expressions whose inputs come from
22957 * the new tuple. We assume these columns won't reference each
22958 * other, so that there's no ordering dependency.
22959 */
22961 insertslot, econtext);
22962
22963 /* Write the tuple out to the new relation. */
22965 ti_options, bistate);
22966
22967 ResetExprContext(econtext);
22968 }
22969
22971 table_endscan(scan);
22972 UnregisterSnapshot(snapshot);
22973
22974 if (tuple_map)
22976
22979 }
22980
22981 FreeExecutorState(estate);
22983 FreeBulkInsertState(bistate);
22984
22986
22987 /*
22988 * We don't need to process this newPartRel since we already processed it
22989 * here, so delete the ALTER TABLE queue for it.
22990 */
22991 foreach(ltab, *wqueue)
22992 {
22993 tab = (AlteredTableInfo *) lfirst(ltab);
22994 if (tab->relid == RelationGetRelid(newPartRel))
22995 {
22997 break;
22998 }
22999 }
23000}
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(), memcpy(), MemoryContextSwitchTo(), NoLock, RegisterSnapshot(), RelationGetDescr, RelationGetRelid, AlteredTableInfo::relid, ResetExprContext, slot_getallattrs(), SO_NONE, 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 8179 of file tablecmds.c.

8180{
8182
8183 nnulltest->arg = (Expr *) makeVar(1,
8184 attr->attnum,
8185 attr->atttypid,
8186 attr->atttypmod,
8187 attr->attcollation,
8188 0);
8189 nnulltest->nulltesttype = IS_NOT_NULL;
8190
8191 /*
8192 * argisrow = false is correct even for a composite column, because
8193 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8194 * case, just IS DISTINCT FROM NULL.
8195 */
8196 nnulltest->argisrow = false;
8197 nnulltest->location = -1;
8198
8200 {
8202 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8203 RelationGetRelationName(rel), NameStr(attr->attname))));
8204 return true;
8205 }
8206
8207 return false;
8208}
@ IS_NOT_NULL
Definition primnodes.h:1992
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 20312 of file tablecmds.c.

20314{
20316 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20317 int i;
20318
20319 if (constr && constr->has_not_null)
20320 {
20321 int natts = scanrel->rd_att->natts;
20322
20323 for (i = 1; i <= natts; i++)
20324 {
20325 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20326
20327 /* invalid not-null constraint must be ignored here */
20328 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20329 {
20332
20333 ntest->arg = (Expr *) makeVar(1,
20334 i,
20335 wholeatt->atttypid,
20336 wholeatt->atttypmod,
20337 wholeatt->attcollation,
20338 0);
20339 ntest->nulltesttype = IS_NOT_NULL;
20340
20341 /*
20342 * argisrow=false is correct even for a composite column,
20343 * because attnotnull does not represent a SQL-spec IS NOT
20344 * NULL test in such a case, just IS DISTINCT FROM NULL.
20345 */
20346 ntest->argisrow = false;
20347 ntest->location = -1;
20349 }
20350 }
20351 }
20352
20354}

References CompactAttribute::attisdropped, CompactAttribute::attnullability, 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 19568 of file tablecmds.c.

19569{
19570 ListCell *l;
19573
19574 foreach(l, on_commits)
19575 {
19577
19578 /* Ignore entry if already dropped in this xact */
19579 if (oc->deleting_subid != InvalidSubTransactionId)
19580 continue;
19581
19582 switch (oc->oncommit)
19583 {
19584 case ONCOMMIT_NOOP:
19586 /* Do nothing (there shouldn't be such entries, actually) */
19587 break;
19589
19590 /*
19591 * If this transaction hasn't accessed any temporary
19592 * relations, we can skip truncating ON COMMIT DELETE ROWS
19593 * tables, as they must still be empty.
19594 */
19597 break;
19598 case ONCOMMIT_DROP:
19600 break;
19601 }
19602 }
19603
19604 /*
19605 * Truncate relations before dropping so that all dependencies between
19606 * relations are removed after they are worked on. Doing it like this
19607 * might be a waste as it is possible that a relation being truncated will
19608 * be dropped anyway due to its parent being dropped, but this makes the
19609 * code more robust because of not having to re-check that the relation
19610 * exists at truncation time.
19611 */
19612 if (oids_to_truncate != NIL)
19614
19615 if (oids_to_drop != NIL)
19616 {
19618
19619 foreach(l, oids_to_drop)
19620 {
19621 ObjectAddress object;
19622
19623 object.classId = RelationRelationId;
19624 object.objectId = lfirst_oid(l);
19625 object.objectSubId = 0;
19626
19628
19630 }
19631
19632 /*
19633 * Object deletion might involve toast table access (to clean up
19634 * toasted catalog entries), so ensure we have a valid snapshot.
19635 */
19637
19638 /*
19639 * Since this is an automatic drop, rather than one directly initiated
19640 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19641 */
19644
19646
19647#ifdef USE_ASSERT_CHECKING
19648
19649 /*
19650 * Note that table deletion will call remove_on_commit_action, so the
19651 * entry should get marked as deleted.
19652 */
19653 foreach(l, on_commits)
19654 {
19656
19657 if (oc->oncommit != ONCOMMIT_DROP)
19658 continue;
19659
19660 Assert(oc->deleting_subid != InvalidSubTransactionId);
19661 }
19662#endif
19663 }
19664}
#define PERFORM_DELETION_QUIETLY
Definition dependency.h:94
void heap_truncate(List *relids)
Definition heap.c:3611
@ 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:138
#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 13373 of file tablecmds.c.

13376{
13378 AlteredTableInfo *tab;
13381
13382 List *children = NIL;
13383 ListCell *child;
13385 Datum val;
13386 char *conbin;
13387
13389 Assert(con->contype == CONSTRAINT_CHECK);
13390
13391 /*
13392 * If we're recursing, the parent has already done this, so skip it. Also,
13393 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13394 * for it in the children.
13395 */
13396 if (!recursing && !con->connoinherit)
13397 children = find_all_inheritors(RelationGetRelid(rel),
13398 lockmode, NULL);
13399
13400 /*
13401 * For CHECK constraints, we must ensure that we only mark the constraint
13402 * as validated on the parent if it's already validated on the children.
13403 *
13404 * We recurse before validating on the parent, to reduce risk of
13405 * deadlocks.
13406 */
13407 foreach(child, children)
13408 {
13409 Oid childoid = lfirst_oid(child);
13411
13412 if (childoid == RelationGetRelid(rel))
13413 continue;
13414
13415 /*
13416 * If we are told not to recurse, there had better not be any child
13417 * tables, because we can't mark the constraint on the parent valid
13418 * unless it is valid for all child tables.
13419 */
13420 if (!recurse)
13421 ereport(ERROR,
13423 errmsg("constraint must be validated on child tables too")));
13424
13425 /* find_all_inheritors already got lock */
13427
13429 true, lockmode);
13431 }
13432
13433 /* Queue validation for phase 3 */
13435 newcon->name = constrName;
13436 newcon->contype = CONSTR_CHECK;
13437 newcon->refrelid = InvalidOid;
13438 newcon->refindid = InvalidOid;
13439 newcon->conid = con->oid;
13440
13445
13446 /* Find or create work queue entry for this table */
13447 tab = ATGetQueueEntry(wqueue, rel);
13448 tab->constraints = lappend(tab->constraints, newcon);
13449
13450 /*
13451 * Invalidate relcache so that others see the new validated constraint.
13452 */
13454
13455 /*
13456 * Now update the catalog, while we have the door open.
13457 */
13460 copy_con->convalidated = true;
13462
13464
13466}

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

13253{
13255 AlteredTableInfo *tab;
13258
13259 /* since this function recurses, it could be driven to stack overflow */
13261
13263 Assert(con->contype == CONSTRAINT_FOREIGN);
13264 Assert(!con->convalidated);
13265
13266 /*
13267 * Add the validation to phase 3's queue; not needed for partitioned
13268 * tables themselves, only for their partitions.
13269 *
13270 * When the referenced table (pkrelid) is partitioned, the referencing
13271 * table (fkrel) has one pg_constraint row pointing to each partition
13272 * thereof. These rows are there only to support action triggers and no
13273 * table scan is needed, therefore skip this for them as well.
13274 */
13275 if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13276 con->confrelid == pkrelid)
13277 {
13280
13281 /* Queue validation for phase 3 */
13283 /* for now this is all we need */
13284 fkconstraint->conname = pstrdup(NameStr(con->conname));
13285
13287 newcon->name = fkconstraint->conname;
13288 newcon->contype = CONSTR_FOREIGN;
13289 newcon->refrelid = con->confrelid;
13290 newcon->refindid = con->conindid;
13291 newcon->conid = con->oid;
13292 newcon->qual = (Node *) fkconstraint;
13293
13294 /* Find or create work queue entry for this table */
13296 tab->constraints = lappend(tab->constraints, newcon);
13297 }
13298
13299 /*
13300 * If the table at either end of the constraint is partitioned, we need to
13301 * recurse and handle every unvalidated constraint that is a child of this
13302 * constraint.
13303 */
13304 if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13305 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13306 {
13307 ScanKeyData pkey;
13310
13311 ScanKeyInit(&pkey,
13314 ObjectIdGetDatum(con->oid));
13315
13317 true, NULL, 1, &pkey);
13318
13320 {
13323
13325
13326 /*
13327 * If the child constraint has already been validated, no further
13328 * action is required for it or its descendants, as they are all
13329 * valid.
13330 */
13331 if (childcon->convalidated)
13332 continue;
13333
13334 childrel = table_open(childcon->conrelid, lockmode);
13335
13336 /*
13337 * NB: Note that pkrelid should be passed as-is during recursion,
13338 * as it is required to identify the root referenced table.
13339 */
13341 childtup, lockmode);
13343 }
13344
13346 }
13347
13348 /*
13349 * Now mark the pg_constraint row as validated (even if we didn't check,
13350 * notably the ones for partitions on the referenced side).
13351 *
13352 * We rely on transaction abort to roll back this change if phase 3
13353 * ultimately finds violating rows. This is a bit ugly.
13354 */
13357 copy_con->convalidated = true;
13359
13361
13363}

References Assert, ATGetQueueEntry(), BTEqualStrategyNumber, CatalogTupleUpdate(), check_stack_depth(), 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 13476 of file tablecmds.c.

13479{
13481 AlteredTableInfo *tab;
13484 List *children = NIL;
13486 char *colname;
13487
13489 Assert(con->contype == CONSTRAINT_NOTNULL);
13490
13492
13493 /*
13494 * If we're recursing, we've already done this for parent, so skip it.
13495 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13496 * look for it in the children.
13497 *
13498 * We recurse before validating on the parent, to reduce risk of
13499 * deadlocks.
13500 */
13501 if (!recursing && !con->connoinherit)
13502 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13503
13504 colname = get_attname(RelationGetRelid(rel), attnum, false);
13505 foreach_oid(childoid, children)
13506 {
13510 char *conname;
13511
13512 if (childoid == RelationGetRelid(rel))
13513 continue;
13514
13515 /*
13516 * If we are told not to recurse, there had better not be any child
13517 * tables, because we can't mark the constraint on the parent valid
13518 * unless it is valid for all child tables.
13519 */
13520 if (!recurse)
13521 ereport(ERROR,
13523 errmsg("constraint must be validated on child tables too"));
13524
13525 /*
13526 * The column on child might have a different attnum, so search by
13527 * column name.
13528 */
13530 if (!contup)
13531 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13532 colname, get_rel_name(childoid));
13534 if (childcon->convalidated)
13535 continue;
13536
13537 /* find_all_inheritors already got lock */
13539 conname = pstrdup(NameStr(childcon->conname));
13540
13541 /* XXX improve ATExecValidateConstraint API to avoid double search */
13543 false, true, lockmode);
13545 }
13546
13547 /* Set attnotnull appropriately without queueing another validation */
13548 set_attnotnull(NULL, rel, attnum, true, false);
13549
13550 tab = ATGetQueueEntry(wqueue, rel);
13551 tab->verify_new_notnull = true;
13552
13553 /*
13554 * Invalidate relcache so that others see the new validated constraint.
13555 */
13557
13558 /*
13559 * Now update the catalogs, while we have the door open.
13560 */
13563 copy_con->convalidated = true;
13565
13567
13569}

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

20433{
20434 /*
20435 * Based on the table's existing constraints, determine whether or not we
20436 * may skip scanning the table.
20437 */
20439 {
20440 if (!validate_default)
20442 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20443 RelationGetRelationName(scanrel))));
20444 else
20446 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20447 RelationGetRelationName(scanrel))));
20448 return;
20449 }
20450
20451 /*
20452 * Constraints proved insufficient. For plain relations, queue a
20453 * validation item now; for partitioned tables, recurse to process each
20454 * partition.
20455 */
20456 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20457 {
20458 AlteredTableInfo *tab;
20459
20460 /* Grab a work queue entry. */
20461 tab = ATGetQueueEntry(wqueue, scanrel);
20464 tab->validate_default = validate_default;
20465 }
20466 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20467 {
20468 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20469 int i;
20470
20471 for (i = 0; i < partdesc->nparts; i++)
20472 {
20475
20476 /*
20477 * This is the minimum lock we need to prevent deadlocks.
20478 */
20480
20481 /*
20482 * Adjust the constraint for scanrel so that it matches this
20483 * partition's attribute numbers.
20484 */
20487 part_rel, scanrel);
20488
20491 validate_default);
20492 table_close(part_rel, NoLock); /* keep lock till commit */
20493 }
20494 }
20495}
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 19834 of file tablecmds.c.

19836{
19837 Node *stmt = (Node *) arg;
19838 ObjectType reltype;
19839 HeapTuple tuple;
19842 char relkind;
19843
19844 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19845 if (!HeapTupleIsValid(tuple))
19846 return; /* concurrently dropped */
19848 relkind = classform->relkind;
19849
19850 /* Must own relation. */
19853
19854 /* No system table modifications unless explicitly allowed. */
19856 ereport(ERROR,
19858 errmsg("permission denied: \"%s\" is a system catalog",
19859 rv->relname)));
19860
19861 /*
19862 * Extract the specified relation type from the statement parse tree.
19863 *
19864 * Also, for ALTER .. RENAME, check permissions: the user must (still)
19865 * have CREATE rights on the containing namespace.
19866 */
19867 if (IsA(stmt, RenameStmt))
19868 {
19871 if (aclresult != ACLCHECK_OK)
19873 get_namespace_name(classform->relnamespace));
19874 reltype = ((RenameStmt *) stmt)->renameType;
19875 }
19876 else if (IsA(stmt, AlterObjectSchemaStmt))
19877 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19878
19879 else if (IsA(stmt, AlterTableStmt))
19880 reltype = ((AlterTableStmt *) stmt)->objtype;
19881 else
19882 {
19883 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19884 reltype = OBJECT_TABLE; /* placate compiler */
19885 }
19886
19887 /*
19888 * For compatibility with prior releases, we allow ALTER TABLE to be used
19889 * with most other types of relations (but not composite types). We allow
19890 * similar flexibility for ALTER INDEX in the case of RENAME, but not
19891 * otherwise. Otherwise, the user must select the correct form of the
19892 * command for the relation at issue.
19893 */
19894 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19895 ereport(ERROR,
19897 errmsg("\"%s\" is not a sequence", rv->relname)));
19898
19899 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19900 ereport(ERROR,
19902 errmsg("\"%s\" is not a view", rv->relname)));
19903
19904 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19905 ereport(ERROR,
19907 errmsg("\"%s\" is not a materialized view", rv->relname)));
19908
19909 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19910 ereport(ERROR,
19912 errmsg("\"%s\" is not a foreign table", rv->relname)));
19913
19914 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19915 ereport(ERROR,
19917 errmsg("\"%s\" is not a composite type", rv->relname)));
19918
19919 if (reltype == OBJECT_PROPGRAPH && relkind != RELKIND_PROPGRAPH)
19920 ereport(ERROR,
19922 errmsg("\"%s\" is not a property graph", rv->relname)));
19923
19924 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19925 relkind != RELKIND_PARTITIONED_INDEX
19926 && !IsA(stmt, RenameStmt))
19927 ereport(ERROR,
19929 errmsg("\"%s\" is not an index", rv->relname)));
19930
19931 /*
19932 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19933 * TYPE for that.
19934 */
19935 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19936 ereport(ERROR,
19938 errmsg("\"%s\" is a composite type", rv->relname),
19939 /* translator: %s is an SQL ALTER command */
19940 errhint("Use %s instead.",
19941 "ALTER TYPE")));
19942
19943 /*
19944 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19945 * to a different schema, such as indexes and TOAST tables.
19946 */
19948 {
19949 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19950 ereport(ERROR,
19952 errmsg("cannot change schema of index \"%s\"",
19953 rv->relname),
19954 errhint("Change the schema of the table instead.")));
19955 else if (relkind == RELKIND_COMPOSITE_TYPE)
19956 ereport(ERROR,
19958 errmsg("cannot change schema of composite type \"%s\"",
19959 rv->relname),
19960 /* translator: %s is an SQL ALTER command */
19961 errhint("Use %s instead.",
19962 "ALTER TYPE")));
19963 else if (relkind == RELKIND_TOASTVALUE)
19964 ereport(ERROR,
19966 errmsg("cannot change schema of TOAST table \"%s\"",
19967 rv->relname),
19968 errhint("Change the schema of the table instead.")));
19969 }
19970
19971 ReleaseSysCache(tuple);
19972}
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition catalog.c:86
ObjectType
@ OBJECT_PROPGRAPH
@ 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_PROPGRAPH, 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 21841 of file tablecmds.c.

21843{
21846 HeapTuple tuple;
21847
21848 state = (struct AttachIndexCallbackState *) arg;
21849
21850 if (!state->lockedParentTbl)
21851 {
21852 LockRelationOid(state->parentTblOid, AccessShareLock);
21853 state->lockedParentTbl = true;
21854 }
21855
21856 /*
21857 * If we previously locked some other heap, and the name we're looking up
21858 * no longer refers to an index on that relation, release the now-useless
21859 * lock. XXX maybe we should do *after* we verify whether the index does
21860 * not actually belong to the same relation ...
21861 */
21862 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
21863 {
21864 UnlockRelationOid(state->partitionOid, AccessShareLock);
21865 state->partitionOid = InvalidOid;
21866 }
21867
21868 /* Didn't find a relation, so no need for locking or permission checks. */
21869 if (!OidIsValid(relOid))
21870 return;
21871
21872 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
21873 if (!HeapTupleIsValid(tuple))
21874 return; /* concurrently dropped, so nothing to do */
21876 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
21877 classform->relkind != RELKIND_INDEX)
21878 ereport(ERROR,
21880 errmsg("\"%s\" is not an index", rv->relname)));
21881 ReleaseSysCache(tuple);
21882
21883 /*
21884 * Since we need only examine the heap's tupledesc, an access share lock
21885 * on it (preventing any DDL) is sufficient.
21886 */
21887 state->partitionOid = IndexGetRelation(relOid, false);
21888 LockRelationOid(state->partitionOid, AccessShareLock);
21889}
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 1757 of file tablecmds.c.

1759{
1760 HeapTuple tuple;
1762 char expected_relkind;
1763 bool is_partition;
1766 bool invalid_system_index = false;
1767
1768 state = (struct DropRelationCallbackState *) arg;
1769 heap_lockmode = state->heap_lockmode;
1770
1771 /*
1772 * If we previously locked some other index's heap, and the name we're
1773 * looking up no longer refers to that relation, release the now-useless
1774 * lock.
1775 */
1776 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1777 {
1779 state->heapOid = InvalidOid;
1780 }
1781
1782 /*
1783 * Similarly, if we previously locked some other partition's heap, and the
1784 * name we're looking up no longer refers to that relation, release the
1785 * now-useless lock.
1786 */
1787 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1788 {
1790 state->partParentOid = InvalidOid;
1791 }
1792
1793 /* Didn't find a relation, so no need for locking or permission checks. */
1794 if (!OidIsValid(relOid))
1795 return;
1796
1797 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1798 if (!HeapTupleIsValid(tuple))
1799 return; /* concurrently dropped, so nothing to do */
1801 is_partition = classform->relispartition;
1802
1803 /* Pass back some data to save lookups in RemoveRelations */
1804 state->actual_relkind = classform->relkind;
1805 state->actual_relpersistence = classform->relpersistence;
1806
1807 /*
1808 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1809 * but RemoveRelations() can only pass one relkind for a given relation.
1810 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1811 * That means we must be careful before giving the wrong type error when
1812 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1813 * exists with indexes.
1814 */
1815 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1817 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1819 else
1820 expected_relkind = classform->relkind;
1821
1822 if (state->expected_relkind != expected_relkind)
1824 state->expected_relkind);
1825
1826 /* Allow DROP to either table owner or schema owner */
1831 rel->relname);
1832
1833 /*
1834 * Check the case of a system index that might have been invalidated by a
1835 * failed concurrent process and allow its drop. For the time being, this
1836 * only concerns indexes of toast relations that became invalid during a
1837 * REINDEX CONCURRENTLY process.
1838 */
1839 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1840 {
1843 bool indisvalid;
1844
1847 {
1848 ReleaseSysCache(tuple);
1849 return;
1850 }
1851
1853 indisvalid = indexform->indisvalid;
1855
1856 /* Mark object as being an invalid index of system catalogs */
1857 if (!indisvalid)
1858 invalid_system_index = true;
1859 }
1860
1861 /* In the case of an invalid index, it is fine to bypass this check */
1863 ereport(ERROR,
1865 errmsg("permission denied: \"%s\" is a system catalog",
1866 rel->relname)));
1867
1868 ReleaseSysCache(tuple);
1869
1870 /*
1871 * In DROP INDEX, attempt to acquire lock on the parent table before
1872 * locking the index. index_drop() will need this anyway, and since
1873 * regular queries lock tables before their indexes, we risk deadlock if
1874 * we do it the other way around. No error if we don't find a pg_index
1875 * entry, though --- the relation may have been dropped. Note that this
1876 * code will execute for either plain or partitioned indexes.
1877 */
1879 relOid != oldRelOid)
1880 {
1881 state->heapOid = IndexGetRelation(relOid, true);
1882 if (OidIsValid(state->heapOid))
1884 }
1885
1886 /*
1887 * Similarly, if the relation is a partition, we must acquire lock on its
1888 * parent before locking the partition. That's because queries lock the
1889 * parent before its partitions, so we risk deadlock if we do it the other
1890 * way around.
1891 */
1892 if (is_partition && relOid != oldRelOid)
1893 {
1894 state->partParentOid = get_partition_parent(relOid, true);
1895 if (OidIsValid(state->partParentOid))
1896 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1897 }
1898}
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:1562

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

4048{
4049 HeapTuple tuple;
4051
4052 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
4053 if (!HeapTupleIsValid(tuple))
4054 return; /* concurrently dropped */
4055 form = (Form_pg_class) GETSTRUCT(tuple);
4056 renameatt_check(relid, form, false);
4057 ReleaseSysCache(tuple);
4058}
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition tablecmds.c:3852

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

19780{
19781 HeapTuple tuple;
19782
19783 /* Nothing to do if the relation was not found. */
19784 if (!OidIsValid(relId))
19785 return;
19786
19787 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19788 if (!HeapTupleIsValid(tuple)) /* should not happen */
19789 elog(ERROR, "cache lookup failed for relation %u", relId);
19790
19793
19794 ReleaseSysCache(tuple);
19795}

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

19744{
19745 char relkind;
19747
19748 /* Nothing to do if the relation was not found. */
19749 if (!OidIsValid(relId))
19750 return;
19751
19752 /*
19753 * If the relation does exist, check whether it's an index. But note that
19754 * the relation might have been dropped between the time we did the name
19755 * lookup and now. In that case, there's nothing to do.
19756 */
19757 relkind = get_rel_relkind(relId);
19758 if (!relkind)
19759 return;
19760 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19761 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19762 ereport(ERROR,
19764 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19765
19766 /* Check permissions */
19768 if (aclresult != ACLCHECK_OK)
19771 relation->relname);
19772}
#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 ExecRefreshMatView(), process_single_relation(), and ReindexTable().

◆ RangeVarCallbackOwnsRelation()

void RangeVarCallbackOwnsRelation ( const RangeVar relation,
Oid  relId,
Oid  oldRelId,
void arg 
)

Definition at line 19802 of file tablecmds.c.

19804{
19805 HeapTuple tuple;
19806
19807 /* Nothing to do if the relation was not found. */
19808 if (!OidIsValid(relId))
19809 return;
19810
19811 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19812 if (!HeapTupleIsValid(tuple)) /* should not happen */
19813 elog(ERROR, "cache lookup failed for relation %u", relId);
19814
19817 relation->relname);
19818
19819 if (!allowSystemTableMods &&
19820 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19821 ereport(ERROR,
19823 errmsg("permission denied: \"%s\" is a system catalog",
19824 relation->relname)));
19825
19826 ReleaseSysCache(tuple);
19827}

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 AlterPropGraph(), AlterSequence(), CreatePropGraph(), 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 16098 of file tablecmds.c.

16101{
16102 CommentStmt *cmd;
16103 char *comment_str;
16105
16106 /* Look for comment for object wanted, and leave if none */
16108 if (comment_str == NULL)
16109 return;
16110
16111 /* Build CommentStmt node, copying all input data for safety */
16112 cmd = makeNode(CommentStmt);
16113 if (rel)
16114 {
16116 cmd->object = (Node *)
16119 makeString(pstrdup(conname)));
16120 }
16121 else
16122 {
16124 cmd->object = (Node *)
16126 makeString(pstrdup(conname)));
16127 }
16128 cmd->comment = comment_str;
16129
16130 /* Append it to list of commands */
16132 newcmd->subtype = AT_ReAddComment;
16133 newcmd->def = (Node *) cmd;
16134 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
16135}
TypeName * makeTypeNameFromNameList(List *names)
Definition makefuncs.c:531
@ OBJECT_TABCONSTRAINT
@ OBJECT_DOMCONSTRAINT
#define list_make3(x1, x2, x3)
Definition pg_list.h:248
#define list_make2(x1, x2)
Definition pg_list.h:246
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 22068 of file tablecmds.c.

22069{
22071
22072 existingIdx = index_get_partition(partitionTbl,
22073 RelationGetRelid(parentIdx));
22075 ereport(ERROR,
22077 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22079 RelationGetRelationName(parentIdx)),
22080 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
22082 RelationGetRelationName(partitionTbl))));
22083}

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

19510{
19513
19514 /*
19515 * We needn't bother registering the relation unless there is an ON COMMIT
19516 * action we need to take.
19517 */
19518 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19519 return;
19520
19522
19524 oc->relid = relid;
19525 oc->oncommit = action;
19526 oc->creating_subid = GetCurrentSubTransactionId();
19527 oc->deleting_subid = InvalidSubTransactionId;
19528
19529 /*
19530 * We use lcons() here so that ON COMMIT actions are processed in reverse
19531 * order of registration. That might not be essential but it seems
19532 * reasonable.
19533 */
19535
19537}
List * lcons(void *datum, List *list)
Definition list.c:495
MemoryContext CacheMemoryContext
Definition mcxt.c:170

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

18652{
18659 ListCell *index;
18660
18661 /*
18662 * Check whether relreplident has changed, and update it if so.
18663 */
18668 elog(ERROR, "cache lookup failed for relation \"%s\"",
18671 if (pg_class_form->relreplident != ri_type)
18672 {
18673 pg_class_form->relreplident = ri_type;
18675 }
18678
18679 /*
18680 * Update the per-index indisreplident flags correctly.
18681 */
18683 foreach(index, RelationGetIndexList(rel))
18684 {
18686 bool dirty = false;
18687
18691 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18693
18694 if (thisIndexOid == indexOid)
18695 {
18696 /* Set the bit if not already set. */
18697 if (!pg_index_form->indisreplident)
18698 {
18699 dirty = true;
18700 pg_index_form->indisreplident = true;
18701 }
18702 }
18703 else
18704 {
18705 /* Unset the bit if set. */
18706 if (pg_index_form->indisreplident)
18707 {
18708 dirty = true;
18709 pg_index_form->indisreplident = false;
18710 }
18711 }
18712
18713 if (dirty)
18714 {
18717 InvalidOid, is_internal);
18718
18719 /*
18720 * Invalidate the relcache for the table, so that after we commit
18721 * all sessions will refresh the table's replica identity index
18722 * before attempting any UPDATE or DELETE on the table. (If we
18723 * changed the table's pg_class row above, then a relcache inval
18724 * is already queued due to that; but we might not have.)
18725 */
18727 }
18729 }
18730
18732}
Definition type.h:97

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

15301{
15303 ScanKeyData key[3];
15304 SysScanDesc scan;
15306
15307 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15308
15310
15311 ScanKeyInit(&key[0],
15315 ScanKeyInit(&key[1],
15319 ScanKeyInit(&key[2],
15323
15325 NULL, 3, key);
15326
15328 {
15331
15332 foundObject.classId = foundDep->classid;
15333 foundObject.objectId = foundDep->objid;
15334 foundObject.objectSubId = foundDep->objsubid;
15335
15336 switch (foundObject.classId)
15337 {
15338 case RelationRelationId:
15339 {
15340 char relKind = get_rel_relkind(foundObject.objectId);
15341
15342 if (relKind == RELKIND_INDEX ||
15344 {
15345 Assert(foundObject.objectSubId == 0);
15347 }
15348 else if (relKind == RELKIND_SEQUENCE)
15349 {
15350 /*
15351 * This must be a SERIAL column's sequence. We need
15352 * not do anything to it.
15353 */
15354 Assert(foundObject.objectSubId == 0);
15355 }
15356 else
15357 {
15358 /* Not expecting any other direct dependencies... */
15359 elog(ERROR, "unexpected object depending on column: %s",
15361 }
15362 break;
15363 }
15364
15366 Assert(foundObject.objectSubId == 0);
15368 break;
15369
15371
15372 /*
15373 * A new-style SQL function can depend on a column, if that
15374 * column is referenced in the parsed function body. Ideally
15375 * we'd automatically update the function by deparsing and
15376 * reparsing it, but that's risky and might well fail anyhow.
15377 * FIXME someday.
15378 *
15379 * This is only a problem for AT_AlterColumnType, not
15380 * AT_SetExpression.
15381 */
15382 if (subtype == AT_AlterColumnType)
15383 ereport(ERROR,
15385 errmsg("cannot alter type of a column used by a function or procedure"),
15386 errdetail("%s depends on column \"%s\"",
15388 colName)));
15389 break;
15390
15391 case RewriteRelationId:
15392
15393 /*
15394 * View/rule bodies have pretty much the same issues as
15395 * function bodies. FIXME someday.
15396 */
15397 if (subtype == AT_AlterColumnType)
15398 ereport(ERROR,
15400 errmsg("cannot alter type of a column used by a view or rule"),
15401 errdetail("%s depends on column \"%s\"",
15403 colName)));
15404 break;
15405
15406 case TriggerRelationId:
15407
15408 /*
15409 * A trigger can depend on a column because the column is
15410 * specified as an update target, or because the column is
15411 * used in the trigger's WHEN condition. The first case would
15412 * not require any extra work, but the second case would
15413 * require updating the WHEN expression, which has the same
15414 * issues as above. Since we can't easily tell which case
15415 * applies, we punt for both. FIXME someday.
15416 */
15417 if (subtype == AT_AlterColumnType)
15418 ereport(ERROR,
15420 errmsg("cannot alter type of a column used in a trigger definition"),
15421 errdetail("%s depends on column \"%s\"",
15423 colName)));
15424 break;
15425
15426 case PolicyRelationId:
15427
15428 /*
15429 * A policy can depend on a column because the column is
15430 * specified in the policy's USING or WITH CHECK qual
15431 * expressions. It might be possible to rewrite and recheck
15432 * the policy expression, but punt for now. It's certainly
15433 * easy enough to remove and recreate the policy; still, FIXME
15434 * someday.
15435 */
15436 if (subtype == AT_AlterColumnType)
15437 ereport(ERROR,
15439 errmsg("cannot alter type of a column used in a policy definition"),
15440 errdetail("%s depends on column \"%s\"",
15442 colName)));
15443 break;
15444
15446 {
15448
15449 if (col.objectId == RelationGetRelid(rel) &&
15450 col.objectSubId == attnum)
15451 {
15452 /*
15453 * Ignore the column's own default expression. The
15454 * caller deals with it.
15455 */
15456 }
15457 else
15458 {
15459 /*
15460 * This must be a reference from the expression of a
15461 * generated column elsewhere in the same table.
15462 * Changing the type/generated expression of a column
15463 * that is used by a generated column is not allowed
15464 * by SQL standard, so just punt for now. It might be
15465 * doable with some thinking and effort.
15466 */
15467 if (subtype == AT_AlterColumnType)
15468 ereport(ERROR,
15470 errmsg("cannot alter type of a column used by a generated column"),
15471 errdetail("Column \"%s\" is used by generated column \"%s\".",
15472 colName,
15473 get_attname(col.objectId,
15474 col.objectSubId,
15475 false))));
15476 }
15477 break;
15478 }
15479
15481
15482 /*
15483 * Give the extended-stats machinery a chance to fix anything
15484 * that this column type change would break.
15485 */
15487 break;
15488
15490
15491 /*
15492 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15493 * clause. Same issues as above. FIXME someday.
15494 */
15495 if (subtype == AT_AlterColumnType)
15496 ereport(ERROR,
15498 errmsg("cannot alter type of a column used by a publication WHERE clause"),
15499 errdetail("%s depends on column \"%s\"",
15501 colName)));
15502 break;
15503
15504 default:
15505
15506 /*
15507 * We don't expect any other sorts of objects to depend on a
15508 * column.
15509 */
15510 elog(ERROR, "unexpected object depending on column: %s",
15512 break;
15513 }
15514 }
15515
15516 systable_endscan(scan);
15518}
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 15540 of file tablecmds.c.

15541{
15542 if (!get_index_isclustered(indoid))
15543 return;
15544
15545 if (tab->clusterOnIndex)
15546 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15547
15548 tab->clusterOnIndex = get_rel_name(indoid);
15549}
bool get_index_isclustered(Oid index_oid)
Definition lsyscache.c:3954

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

15557{
15558 /*
15559 * This de-duplication check is critical for two independent reasons: we
15560 * mustn't try to recreate the same constraint twice, and if a constraint
15561 * depends on more than one column whose type is to be altered, we must
15562 * capture its definition string before applying any of the column type
15563 * changes. ruleutils.c will get confused if we ask again later.
15564 */
15565 if (!list_member_oid(tab->changedConstraintOids, conoid))
15566 {
15567 /* OK, capture the constraint's existing definition string */
15569 Oid indoid;
15570
15571 /*
15572 * It is critical to create not-null constraints ahead of primary key
15573 * indexes; otherwise, the not-null constraint would be created by the
15574 * primary key, and the constraint name would be wrong.
15575 */
15577 {
15578 tab->changedConstraintOids = lcons_oid(conoid,
15582 }
15583 else
15584 {
15585
15587 conoid);
15589 defstring);
15590 }
15591
15592 /*
15593 * For the index of a constraint, if any, remember if it is used for
15594 * the table's replica identity or if it is a clustered index, so that
15595 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15596 * those properties.
15597 */
15598 indoid = get_constraint_index(conoid);
15599 if (OidIsValid(indoid))
15600 {
15602 RememberClusterOnForRebuilding(indoid, tab);
15603 }
15604 }
15605}
List * lcons_oid(Oid datum, List *list)
Definition list.c:531
char * pg_get_constraintdef_command(Oid constraintId)
Definition ruleutils.c:2542
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 15612 of file tablecmds.c.

15613{
15614 /*
15615 * This de-duplication check is critical for two independent reasons: we
15616 * mustn't try to recreate the same index twice, and if an index depends
15617 * on more than one column whose type is to be altered, we must capture
15618 * its definition string before applying any of the column type changes.
15619 * ruleutils.c will get confused if we ask again later.
15620 */
15621 if (!list_member_oid(tab->changedIndexOids, indoid))
15622 {
15623 /*
15624 * Before adding it as an index-to-rebuild, we'd better see if it
15625 * belongs to a constraint, and if so rebuild the constraint instead.
15626 * Typically this check fails, because constraint indexes normally
15627 * have only dependencies on their constraint. But it's possible for
15628 * such an index to also have direct dependencies on table columns,
15629 * for example with a partial exclusion constraint.
15630 */
15631 Oid conoid = get_index_constraint(indoid);
15632
15633 if (OidIsValid(conoid))
15634 {
15636 }
15637 else
15638 {
15639 /* OK, capture the index's existing definition string */
15640 char *defstring = pg_get_indexdef_string(indoid);
15641
15643 indoid);
15645 defstring);
15646
15647 /*
15648 * Remember if this index is used for the table's replica identity
15649 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15650 * can queue up commands necessary to restore those properties.
15651 */
15653 RememberClusterOnForRebuilding(indoid, tab);
15654 }
15655 }
15656}
Oid get_index_constraint(Oid indexId)
Definition pg_depend.c:1192
char * pg_get_indexdef_string(Oid indexrelid)
Definition ruleutils.c:1236

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

15526{
15527 if (!get_index_isreplident(indoid))
15528 return;
15529
15530 if (tab->replicaIdentityIndex)
15531 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15532
15533 tab->replicaIdentityIndex = get_rel_name(indoid);
15534}
bool get_index_isreplident(Oid index_oid)
Definition lsyscache.c:3908

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

15664{
15665 /*
15666 * This de-duplication check is critical for two independent reasons: we
15667 * mustn't try to recreate the same statistics object twice, and if the
15668 * statistics object depends on more than one column whose type is to be
15669 * altered, we must capture its definition string before applying any of
15670 * the type changes. ruleutils.c will get confused if we ask again later.
15671 */
15673 {
15674 /* OK, capture the statistics object's existing definition string */
15676
15678 stxoid);
15680 defstring);
15681 }
15682}
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition ruleutils.c:1985

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

19546{
19547 ListCell *l;
19548
19549 foreach(l, on_commits)
19550 {
19552
19553 if (oc->relid == relid)
19554 {
19556 break;
19557 }
19558 }
19559}
SubTransactionId deleting_subid
Definition tablecmds.c:132

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

18199{
18201 SysScanDesc scan;
18202 ScanKeyData key[3];
18205 AttrMap *attmap;
18206 List *connames;
18207 List *nncolumns;
18208 bool found;
18209 bool is_partitioning;
18210
18212
18217 if (!found)
18218 {
18219 if (is_partitioning)
18220 ereport(ERROR,
18222 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18225 else
18226 ereport(ERROR,
18228 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18231 }
18232
18233 /*
18234 * Search through child columns looking for ones matching parent rel
18235 */
18237 ScanKeyInit(&key[0],
18242 true, NULL, 1, key);
18244 {
18246
18247 /* Ignore if dropped or not inherited */
18248 if (att->attisdropped)
18249 continue;
18250 if (att->attinhcount <= 0)
18251 continue;
18252
18254 NameStr(att->attname)))
18255 {
18256 /* Decrement inhcount and possibly set islocal to true */
18259
18260 copy_att->attinhcount--;
18261 if (copy_att->attinhcount == 0)
18262 copy_att->attislocal = true;
18263
18266 }
18267 }
18268 systable_endscan(scan);
18270
18271 /*
18272 * Likewise, find inherited check and not-null constraints and disinherit
18273 * them. To do this, we first need a list of the names of the parent's
18274 * check constraints. (We cheat a bit by only checking for name matches,
18275 * assuming that the expressions will match.)
18276 *
18277 * For NOT NULL columns, we store column numbers to match, mapping them in
18278 * to the child rel's attribute numbers.
18279 */
18282 false);
18283
18285 ScanKeyInit(&key[0],
18290 true, NULL, 1, key);
18291
18292 connames = NIL;
18293 nncolumns = NIL;
18294
18296 {
18298
18299 if (con->connoinherit)
18300 continue;
18301
18302 if (con->contype == CONSTRAINT_CHECK)
18303 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18304 if (con->contype == CONSTRAINT_NOTNULL)
18305 {
18307
18309 }
18310 }
18311
18312 systable_endscan(scan);
18313
18314 /* Now scan the child's constraints to find matches */
18315 ScanKeyInit(&key[0],
18320 true, NULL, 1, key);
18321
18323 {
18325 bool match = false;
18326
18327 /*
18328 * Match CHECK constraints by name, not-null constraints by column
18329 * number, and ignore all others.
18330 */
18331 if (con->contype == CONSTRAINT_CHECK)
18332 {
18334 {
18335 if (con->contype == CONSTRAINT_CHECK &&
18336 strcmp(NameStr(con->conname), chkname) == 0)
18337 {
18338 match = true;
18340 break;
18341 }
18342 }
18343 }
18344 else if (con->contype == CONSTRAINT_NOTNULL)
18345 {
18347
18349 {
18350 if (prevattno == child_attno)
18351 {
18352 match = true;
18354 break;
18355 }
18356 }
18357 }
18358 else
18359 continue;
18360
18361 if (match)
18362 {
18363 /* Decrement inhcount and possibly set islocal to true */
18366
18367 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18368 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18370
18371 copy_con->coninhcount--;
18372 if (copy_con->coninhcount == 0)
18373 copy_con->conislocal = true;
18374
18377 }
18378 }
18379
18380 /* We should have matched all constraints */
18381 if (connames != NIL || nncolumns != NIL)
18382 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18385
18386 systable_endscan(scan);
18388
18393
18394 /*
18395 * Post alter hook of this inherits. Since object_access_hook doesn't take
18396 * multiple object identifiers, we relay oid of parent relation using
18397 * auxiliary_id argument.
18398 */
18402}
bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending, const char *childname)
bool SearchSysCacheExistsAttName(Oid relid, const char *attname)
Definition syscache.c:518
#define child_dependency_type(child_is_partition)
Definition tablecmds.c:396

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

12001{
12005 SysScanDesc scan;
12007
12008 ScanKeyInit(&key,
12011 ObjectIdGetDatum(conrelid));
12012
12015 true, NULL, 1, &key);
12017 while ((consttup = systable_getnext(scan)) != NULL)
12018 {
12020
12021 if (conform->conparentid != conoid)
12022 continue;
12023 else
12024 {
12025 ObjectAddress addr;
12029
12032
12033 /*
12034 * First we must delete the dependency record that binds the
12035 * constraint records together.
12036 */
12038 conform->oid,
12041 conoid);
12042 Assert(n == 1); /* actually only one is expected */
12043
12044 /*
12045 * Now search for the triggers for this constraint and set them up
12046 * for deletion too
12047 */
12053 true, NULL, 1, &key2);
12054 while ((trigtup = systable_getnext(scan2)) != NULL)
12055 {
12059 }
12061 }
12062 }
12063 /* make the dependency deletions visible */
12067 systable_endscan(scan);
12068}
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:249
long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype, Oid refclassId, Oid refobjectId)
Definition pg_depend.c:411

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

1590{
1591 ObjectAddresses *objects;
1592 char relkind;
1593 ListCell *cell;
1594 int flags = 0;
1595 LOCKMODE lockmode = AccessExclusiveLock;
1596
1597 /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
1598 if (drop->concurrent)
1599 {
1600 /*
1601 * Note that for temporary relations this lock may get upgraded later
1602 * on, but as no other session can access a temporary relation, this
1603 * is actually fine.
1604 */
1605 lockmode = ShareUpdateExclusiveLock;
1606 Assert(drop->removeType == OBJECT_INDEX);
1607 if (list_length(drop->objects) != 1)
1608 ereport(ERROR,
1610 errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects")));
1611 if (drop->behavior == DROP_CASCADE)
1612 ereport(ERROR,
1614 errmsg("DROP INDEX CONCURRENTLY does not support CASCADE")));
1615 }
1616
1617 /*
1618 * First we identify all the relations, then we delete them in a single
1619 * performMultipleDeletions() call. This is to avoid unwanted DROP
1620 * RESTRICT errors if one of the relations depends on another.
1621 */
1622
1623 /* Determine required relkind */
1624 switch (drop->removeType)
1625 {
1626 case OBJECT_TABLE:
1627 relkind = RELKIND_RELATION;
1628 break;
1629
1630 case OBJECT_INDEX:
1631 relkind = RELKIND_INDEX;
1632 break;
1633
1634 case OBJECT_SEQUENCE:
1635 relkind = RELKIND_SEQUENCE;
1636 break;
1637
1638 case OBJECT_VIEW:
1639 relkind = RELKIND_VIEW;
1640 break;
1641
1642 case OBJECT_MATVIEW:
1643 relkind = RELKIND_MATVIEW;
1644 break;
1645
1647 relkind = RELKIND_FOREIGN_TABLE;
1648 break;
1649
1650 case OBJECT_PROPGRAPH:
1651 relkind = RELKIND_PROPGRAPH;
1652 break;
1653
1654 default:
1655 elog(ERROR, "unrecognized drop object type: %d",
1656 (int) drop->removeType);
1657 relkind = 0; /* keep compiler quiet */
1658 break;
1659 }
1660
1661 /* Lock and validate each relation; build a list of object addresses */
1662 objects = new_object_addresses();
1663
1664 foreach(cell, drop->objects)
1665 {
1666 RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell));
1667 Oid relOid;
1668 ObjectAddress obj;
1670
1671 /*
1672 * These next few steps are a great deal like relation_openrv, but we
1673 * don't bother building a relcache entry since we don't need it.
1674 *
1675 * Check for shared-cache-inval messages before trying to access the
1676 * relation. This is needed to cover the case where the name
1677 * identifies a rel that has been dropped and recreated since the
1678 * start of our transaction: if we don't flush the old syscache entry,
1679 * then we'll latch onto that entry and suffer an error later.
1680 */
1682
1683 /* Look up the appropriate relation using namespace search. */
1684 state.expected_relkind = relkind;
1685 state.heap_lockmode = drop->concurrent ?
1687 /* We must initialize these fields to show that no locks are held: */
1688 state.heapOid = InvalidOid;
1689 state.partParentOid = InvalidOid;
1690
1691 relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1693 &state);
1694
1695 /* Not there? */
1696 if (!OidIsValid(relOid))
1697 {
1698 DropErrorMsgNonExistent(rel, relkind, drop->missing_ok);
1699 continue;
1700 }
1701
1702 /*
1703 * Decide if concurrent mode needs to be used here or not. The
1704 * callback retrieved the rel's persistence for us.
1705 */
1706 if (drop->concurrent &&
1707 state.actual_relpersistence != RELPERSISTENCE_TEMP)
1708 {
1709 Assert(list_length(drop->objects) == 1 &&
1710 drop->removeType == OBJECT_INDEX);
1712 }
1713
1714 /*
1715 * Concurrent index drop cannot be used with partitioned indexes,
1716 * either.
1717 */
1718 if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1719 state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1720 ereport(ERROR,
1722 errmsg("cannot drop partitioned index \"%s\" concurrently",
1723 rel->relname)));
1724
1725 /*
1726 * If we're told to drop a partitioned index, we must acquire lock on
1727 * all the children of its parent partitioned table before proceeding.
1728 * Otherwise we'd try to lock the child index partitions before their
1729 * tables, leading to potential deadlock against other sessions that
1730 * will lock those objects in the other order.
1731 */
1732 if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1733 (void) find_all_inheritors(state.heapOid,
1734 state.heap_lockmode,
1735 NULL);
1736
1737 /* OK, we're ready to delete this one */
1739 obj.objectId = relOid;
1740 obj.objectSubId = 0;
1741
1742 add_exact_object_address(&obj, objects);
1743 }
1744
1745 performMultipleDeletions(objects, drop->behavior, flags);
1746
1747 free_object_addresses(objects);
1748}
#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:1514
static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
Definition tablecmds.c:1757

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_PROPGRAPH, 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 4104 of file tablecmds.c.

4111{
4114 HeapTuple tuple;
4116 ObjectAddress address;
4117
4118 Assert(!myrelid || !mytypid);
4119
4120 if (mytypid)
4121 {
4123 }
4124 else
4125 {
4127
4128 /*
4129 * don't tell it whether we're recursing; we allow changing typed
4130 * tables here
4131 */
4133
4135 }
4136
4138 if (!HeapTupleIsValid(tuple))
4139 elog(ERROR, "cache lookup failed for constraint %u",
4141 con = (Form_pg_constraint) GETSTRUCT(tuple);
4142
4143 if (myrelid &&
4144 (con->contype == CONSTRAINT_CHECK ||
4145 con->contype == CONSTRAINT_NOTNULL) &&
4146 !con->connoinherit)
4147 {
4148 if (recurse)
4149 {
4152 ListCell *lo,
4153 *li;
4154
4157
4159 {
4161 int numparents = lfirst_int(li);
4162
4163 if (childrelid == myrelid)
4164 continue;
4165
4167 }
4168 }
4169 else
4170 {
4171 if (expected_parents == 0 &&
4173 ereport(ERROR,
4175 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4176 oldconname)));
4177 }
4178
4179 if (con->coninhcount > expected_parents)
4180 ereport(ERROR,
4182 errmsg("cannot rename inherited constraint \"%s\"",
4183 oldconname)));
4184 }
4185
4186 if (con->conindid
4187 && (con->contype == CONSTRAINT_PRIMARY
4188 || con->contype == CONSTRAINT_UNIQUE
4189 || con->contype == CONSTRAINT_EXCLUSION))
4190 /* rename the index; this renames the constraint as well */
4191 RenameRelationInternal(con->conindid, newconname, false, true);
4192 else
4194
4196
4197 ReleaseSysCache(tuple);
4198
4199 if (targetrelation)
4200 {
4201 /*
4202 * Invalidate relcache so as others can see the new constraint name.
4203 */
4205
4206 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4207 }
4208
4209 return address;
4210}
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:4104

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

4067{
4068 Oid relid;
4070 ObjectAddress address;
4071
4072 /* lock level taken here should match renameatt_internal */
4074 stmt->missing_ok ? RVR_MISSING_OK : 0,
4076 NULL);
4077
4078 if (!OidIsValid(relid))
4079 {
4081 (errmsg("relation \"%s\" does not exist, skipping",
4082 stmt->relation->relname)));
4083 return InvalidObjectAddress;
4084 }
4085
4086 attnum =
4087 renameatt_internal(relid,
4088 stmt->subname, /* old att name */
4089 stmt->newname, /* new att name */
4090 stmt->relation->inh, /* recursive? */
4091 false, /* recursing? */
4092 0, /* expected inhcount */
4093 stmt->behavior);
4094
4096
4097 return address;
4098}
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition tablecmds.c:3901
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition tablecmds.c:4046

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

3853{
3854 char relkind = classform->relkind;
3855
3856 if (classform->reloftype && !recursing)
3857 ereport(ERROR,
3859 errmsg("cannot rename column of typed table")));
3860
3861 /*
3862 * Renaming the columns of sequences or toast tables doesn't actually
3863 * break anything from the system's point of view, since internal
3864 * references are by attnum. But it doesn't seem right to allow users to
3865 * change names that are hardcoded into the system, hence the following
3866 * restriction.
3867 */
3868 if (relkind != RELKIND_RELATION &&
3869 relkind != RELKIND_VIEW &&
3870 relkind != RELKIND_MATVIEW &&
3871 relkind != RELKIND_COMPOSITE_TYPE &&
3872 relkind != RELKIND_INDEX &&
3873 relkind != RELKIND_PARTITIONED_INDEX &&
3874 relkind != RELKIND_FOREIGN_TABLE &&
3875 relkind != RELKIND_PARTITIONED_TABLE)
3876 ereport(ERROR,
3878 errmsg("cannot rename columns of relation \"%s\"",
3879 NameStr(classform->relname)),
3881
3882 /*
3883 * permissions checking. only the owner of a class can change its schema.
3884 */
3887 NameStr(classform->relname));
3889 ereport(ERROR,
3891 errmsg("permission denied: \"%s\" is a system catalog",
3892 NameStr(classform->relname))));
3893}

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

3908{
3914
3915 /*
3916 * Grab an exclusive lock on the target table, which we will NOT release
3917 * until end of transaction.
3918 */
3921
3922 /*
3923 * if the 'recurse' flag is set then we are supposed to rename this
3924 * attribute in all classes that inherit from 'relname' (as well as in
3925 * 'relname').
3926 *
3927 * any permissions or problems with duplicate attributes will cause the
3928 * whole transaction to abort, which is what we want -- all or nothing.
3929 */
3930 if (recurse)
3931 {
3934 ListCell *lo,
3935 *li;
3936
3937 /*
3938 * we need the number of parents for each child so that the recursive
3939 * calls to renameatt() can determine whether there are any parents
3940 * outside the inheritance hierarchy being processed.
3941 */
3944
3945 /*
3946 * find_all_inheritors does the recursive search of the inheritance
3947 * hierarchy, so all we have to do is process all of the relids in the
3948 * list that it returns.
3949 */
3951 {
3953 int numparents = lfirst_int(li);
3954
3955 if (childrelid == myrelid)
3956 continue;
3957 /* note we need not recurse again */
3959 }
3960 }
3961 else
3962 {
3963 /*
3964 * If we are told not to recurse, there had better not be any child
3965 * tables; else the rename would put them out of step.
3966 *
3967 * expected_parents will only be 0 if we are not already recursing.
3968 */
3969 if (expected_parents == 0 &&
3971 ereport(ERROR,
3973 errmsg("inherited column \"%s\" must be renamed in child tables too",
3974 oldattname)));
3975 }
3976
3977 /* rename attributes in typed tables of composite type */
3978 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3979 {
3981 ListCell *lo;
3982
3985 behavior);
3986
3987 foreach(lo, child_oids)
3988 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3989 }
3990
3992
3995 ereport(ERROR,
3997 errmsg("column \"%s\" does not exist",
3998 oldattname)));
4000
4001 attnum = attform->attnum;
4002 if (attnum <= 0)
4003 ereport(ERROR,
4005 errmsg("cannot rename system column \"%s\"",
4006 oldattname)));
4007
4008 /*
4009 * if the attribute is inherited, forbid the renaming. if this is a
4010 * top-level call to renameatt(), then expected_parents will be 0, so the
4011 * effect of this code will be to prohibit the renaming if the attribute
4012 * is inherited at all. if this is a recursive call to renameatt(),
4013 * expected_parents will be the number of parents the current relation has
4014 * within the inheritance hierarchy being processed, so we'll prohibit the
4015 * renaming only if there are additional parents from elsewhere.
4016 */
4017 if (attform->attinhcount > expected_parents)
4018 ereport(ERROR,
4020 errmsg("cannot rename inherited column \"%s\"",
4021 oldattname)));
4022
4023 /* new name should not already exist */
4025
4026 /* apply the update */
4027 namestrcpy(&(attform->attname), newattname);
4028
4030
4032
4034
4036
4037 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4038
4039 return attnum;
4040}
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 4213 of file tablecmds.c.

4214{
4215 Oid relid = InvalidOid;
4216 Oid typid = InvalidOid;
4217
4218 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4219 {
4220 Relation rel;
4221 HeapTuple tup;
4222
4226 if (!HeapTupleIsValid(tup))
4227 elog(ERROR, "cache lookup failed for type %u", typid);
4230 table_close(rel, NoLock);
4231 }
4232 else
4233 {
4234 /* lock level taken here should match rename_constraint_internal */
4236 stmt->missing_ok ? RVR_MISSING_OK : 0,
4238 NULL);
4239 if (!OidIsValid(relid))
4240 {
4242 (errmsg("relation \"%s\" does not exist, skipping",
4243 stmt->relation->relname)));
4244 return InvalidObjectAddress;
4245 }
4246 }
4247
4248 return
4249 rename_constraint_internal(relid, typid,
4250 stmt->subname,
4251 stmt->newname,
4252 (stmt->relation &&
4253 stmt->relation->inh), /* recursive? */
4254 false, /* recursing? */
4255 0 /* expected inhcount */ );
4256}
void checkDomainOwner(HeapTuple tup)
Definition typecmds.c:3536

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

4264{
4265 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4266 Oid relid;
4267 ObjectAddress address;
4268
4269 /*
4270 * Grab an exclusive lock on the target table, index, sequence, view,
4271 * materialized view, or foreign table, which we will NOT release until
4272 * end of transaction.
4273 *
4274 * Lock level used here should match RenameRelationInternal, to avoid lock
4275 * escalation. However, because ALTER INDEX can be used with any relation
4276 * type, we mustn't believe without verification.
4277 */
4278 for (;;)
4279 {
4280 LOCKMODE lockmode;
4281 char relkind;
4282 bool obj_is_index;
4283
4285
4286 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4287 stmt->missing_ok ? RVR_MISSING_OK : 0,
4289 stmt);
4290
4291 if (!OidIsValid(relid))
4292 {
4294 (errmsg("relation \"%s\" does not exist, skipping",
4295 stmt->relation->relname)));
4296 return InvalidObjectAddress;
4297 }
4298
4299 /*
4300 * We allow mismatched statement and object types (e.g., ALTER INDEX
4301 * to rename a table), but we might've used the wrong lock level. If
4302 * that happens, retry with the correct lock level. We don't bother
4303 * if we already acquired AccessExclusiveLock with an index, however.
4304 */
4305 relkind = get_rel_relkind(relid);
4306 obj_is_index = (relkind == RELKIND_INDEX ||
4307 relkind == RELKIND_PARTITIONED_INDEX);
4309 break;
4310
4311 UnlockRelationOid(relid, lockmode);
4313 }
4314
4315 /* Do the work */
4316 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4317
4318 ObjectAddressSet(address, RelationRelationId, relid);
4319
4320 return address;
4321}

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

4328{
4330 Relation relrelation; /* for RELATION relation */
4335
4336 /*
4337 * Grab a lock on the target relation, which we will NOT release until end
4338 * of transaction. We need at least a self-exclusive lock so that
4339 * concurrent DDL doesn't overwrite the rename if they start updating
4340 * while still seeing the old version. The lock also guards against
4341 * triggering relcache reloads in concurrent sessions, which might not
4342 * handle this information changing under them. For indexes, we can use a
4343 * reduced lock level because RelationReloadIndexInfo() handles indexes
4344 * specially.
4345 */
4348
4349 /*
4350 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4351 */
4353
4355 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4356 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4357 otid = reltup->t_self;
4359
4361 ereport(ERROR,
4363 errmsg("relation \"%s\" already exists",
4364 newrelname)));
4365
4366 /*
4367 * RenameRelation is careful not to believe the caller's idea of the
4368 * relation kind being handled. We don't have to worry about this, but
4369 * let's not be totally oblivious to it. We can process an index as
4370 * not-an-index, but not the other way around.
4371 */
4372 Assert(!is_index ||
4373 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4374 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4375
4376 /*
4377 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4378 * because it's a copy...)
4379 */
4380 namestrcpy(&(relform->relname), newrelname);
4381
4384
4386 InvalidOid, is_internal);
4387
4390
4391 /*
4392 * Also rename the associated type, if any.
4393 */
4394 if (OidIsValid(targetrelation->rd_rel->reltype))
4395 RenameTypeInternal(targetrelation->rd_rel->reltype,
4397
4398 /*
4399 * Also rename the associated constraint, if any.
4400 */
4401 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4402 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4403 {
4405
4408 }
4409
4410 /*
4411 * Close rel, but keep lock!
4412 */
4414}
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 4420 of file tablecmds.c.

4421{
4422 Relation relrelation; /* for RELATION relation */
4425
4426 /*
4427 * Find relation's pg_class tuple.
4428 */
4430
4432 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4433 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4435
4436 /*
4437 * Update pg_class tuple.
4438 */
4439 relform->relrewrite = InvalidOid;
4440
4442
4445}

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

7932{
7933 Form_pg_attribute attr;
7935
7937
7939
7940 /*
7941 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7942 * attribute.
7943 */
7944 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7945 if (attr->attisdropped)
7946 return;
7947
7948 if (!attr->attnotnull)
7949 {
7951 HeapTuple tuple;
7952
7954
7956 if (!HeapTupleIsValid(tuple))
7957 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7958 attnum, RelationGetRelid(rel));
7959
7961 thisatt->attnullability = ATTNULLABLE_VALID;
7962
7963 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7964
7965 attr->attnotnull = true;
7966 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7967
7968 /*
7969 * If the nullness isn't already proven by validated constraints, have
7970 * ALTER TABLE phase 3 test for it.
7971 */
7972 if (queue_validation && wqueue &&
7974 {
7975 AlteredTableInfo *tab;
7976
7977 tab = ATGetQueueEntry(wqueue, rel);
7978 tab->verify_new_notnull = true;
7979 }
7980
7982
7984 heap_freetuple(tuple);
7985 }
7986 else
7987 {
7989 }
7990}
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition tablecmds.c:8179

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

9212{
9213 ListCell *lc;
9214
9215 foreach(lc, RelationGetIndexList(rel))
9216 {
9217 Oid indexoid = lfirst_oid(lc);
9220 HeapTuple tuple;
9221
9222 indrel = index_open(indexoid, lockmode);
9223
9224 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9225 {
9226 if (indrel->rd_index->indkey.values[i] == attnum)
9227 {
9228 indattnum = i + 1;
9229 break;
9230 }
9231 }
9232
9233 if (indattnum == 0)
9234 {
9235 index_close(indrel, lockmode);
9236 continue;
9237 }
9238
9240
9241 if (HeapTupleIsValid(tuple))
9242 {
9244
9245 if (setstorage)
9246 attrtuple->attstorage = newstorage;
9247
9248 if (setcompression)
9249 attrtuple->attcompression = newcompression;
9250
9251 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9252
9254 RelationGetRelid(rel),
9255 attrtuple->attnum);
9256
9257 heap_freetuple(tuple);
9258 }
9259
9260 index_close(indrel, lockmode);
9261 }
9262}

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

3705{
3707 HeapTuple tuple;
3709
3711 ShareUpdateExclusiveLock, false) ||
3713 ShareRowExclusiveLock, true));
3714
3715 /*
3716 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3717 */
3720 if (!HeapTupleIsValid(tuple))
3721 elog(ERROR, "cache lookup failed for relation %u", relationId);
3723
3724 if (classtuple->relhassubclass != relhassubclass)
3725 {
3726 classtuple->relhassubclass = relhassubclass;
3728 }
3729 else
3730 {
3731 /* no need to change tuple, but force relcache rebuild anyway */
3733 }
3734
3735 heap_freetuple(tuple);
3737}
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition inval.c:1666

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

3810{
3812 HeapTuple tuple;
3814 Form_pg_class rd_rel;
3815 Oid reloid = RelationGetRelid(rel);
3816
3818
3819 /* Get a modifiable copy of the relation's pg_class row. */
3821
3823 if (!HeapTupleIsValid(tuple))
3824 elog(ERROR, "cache lookup failed for relation %u", reloid);
3825 otid = tuple->t_self;
3826 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3827
3828 /* Update the pg_class row. */
3829 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3832 rd_rel->relfilenode = newRelFilenumber;
3835
3836 /*
3837 * Record dependency on tablespace. This is only required for relations
3838 * that have no physical storage.
3839 */
3840 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3842 rd_rel->reltablespace);
3843
3844 heap_freetuple(tuple);
3846}
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 23524 of file tablecmds.c.

23526{
23527 /* The FSM is empty, so don't bother using it. */
23528 uint32 ti_options = TABLE_INSERT_SKIP_FSM;
23529 CommandId mycid;
23530 EState *estate;
23532 *listptr2;
23534 ExprContext *econtext;
23535 TableScanDesc scan;
23536 Snapshot snapshot;
23541 *pc;
23542
23543 mycid = GetCurrentCommandId(true);
23544
23545 estate = CreateExecutorState();
23546
23547 forboth(listptr, partlist, listptr2, newPartRels)
23548 {
23550
23552
23553 /* Find the work queue entry for the new partition table: newPartRel. */
23554 pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
23555
23556 buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
23557
23558 if (sps->bound->is_default)
23559 {
23560 /*
23561 * We should not create a structure to check the partition
23562 * constraint for the new DEFAULT partition.
23563 */
23565 }
23566 else
23567 {
23569
23570 /* Build expression execution states for partition check quals. */
23574 (Node *) partConstraint);
23575 /* Make a boolean expression for ExecCheck(). */
23577
23578 /*
23579 * Map the vars in the constraint expression from rel's attnos to
23580 * splitRel's.
23581 */
23583 1, splitRel, rel);
23584
23585 pc->partqualstate =
23587 Assert(pc->partqualstate != NULL);
23588 }
23589
23590 /* Store partition context into a list. */
23592 }
23593
23594 econtext = GetPerTupleExprContext(estate);
23595
23596 /* Create the necessary tuple slot. */
23598
23599 /*
23600 * Map computing for moving attributes of the split partition to the new
23601 * partition (for the first new partition, but other new partitions can
23602 * use the same map).
23603 */
23606 RelationGetDescr(pc->partRel));
23607
23608 /* Scan through the rows. */
23609 snapshot = RegisterSnapshot(GetLatestSnapshot());
23610 scan = table_beginscan(splitRel, snapshot, 0, NULL,
23611 SO_NONE);
23612
23613 /*
23614 * Switch to per-tuple memory context and reset it for each tuple
23615 * produced, so we don't leak memory.
23616 */
23618
23620 {
23621 bool found = false;
23623
23625
23626 econtext->ecxt_scantuple = srcslot;
23627
23628 /* Search partition for the current slot, srcslot. */
23629 foreach(listptr, partContexts)
23630 {
23632
23633 /* skip DEFAULT partition */
23634 if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
23635 {
23636 found = true;
23637 break;
23638 }
23639 }
23640 if (!found)
23641 {
23642 /* Use the DEFAULT partition if it exists. */
23643 if (defaultPartCtx)
23645 else
23646 ereport(ERROR,
23648 errmsg("cannot find partition for split partition row"),
23650 }
23651
23652 if (tuple_map)
23653 {
23654 /* Need to use a map to copy attributes. */
23655 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
23656 }
23657 else
23658 {
23659 /* Extract data from the old tuple. */
23661
23662 /* Copy attributes directly. */
23663 insertslot = pc->dstslot;
23664
23666
23667 memcpy(insertslot->tts_values, srcslot->tts_values,
23668 sizeof(Datum) * srcslot->tts_nvalid);
23669 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23670 sizeof(bool) * srcslot->tts_nvalid);
23671
23673 }
23674
23675 /*
23676 * Constraints and GENERATED expressions might reference the tableoid
23677 * column, so fill tts_tableOid with the desired value. (We must do
23678 * this each time, because it gets overwritten with newrel's OID
23679 * during storing.)
23680 */
23681 insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
23682
23683 /*
23684 * Now, evaluate any generated expressions whose inputs come from the
23685 * new tuple. We assume these columns won't reference each other, so
23686 * that there's no ordering dependency.
23687 */
23689 insertslot, econtext);
23690
23691 /* Write the tuple out to the new relation. */
23692 table_tuple_insert(pc->partRel, insertslot, mycid,
23693 ti_options, pc->bistate);
23694
23695 ResetExprContext(econtext);
23696 }
23697
23699
23700 table_endscan(scan);
23701 UnregisterSnapshot(snapshot);
23702
23703 if (tuple_map)
23705
23707
23708 FreeExecutorState(estate);
23709
23712}
static void deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, uint32 ti_options)
static SplitPartitionContext * createSplitPartitionContext(Relation partRel)

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(), memcpy(), MemoryContextSwitchTo(), NIL, RegisterSnapshot(), RelationGetDescr, RelationGetRelid, ResetExprContext, slot_getallattrs(), SO_NONE, 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 2519 of file tablecmds.c.

2520{
2521 switch (c)
2522 {
2523 case TYPSTORAGE_PLAIN:
2524 return "PLAIN";
2526 return "EXTERNAL";
2528 return "EXTENDED";
2529 case TYPSTORAGE_MAIN:
2530 return "MAIN";
2531 default:
2532 return "???";
2533 }
2534}
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 3578 of file tablecmds.c.

3580{
3581 Relation relation;
3583 ListCell *entry;
3584
3585 /*
3586 * sanity checks
3587 */
3589
3590 if (supers == NIL)
3591 return;
3592
3593 /*
3594 * Store INHERITS information in pg_inherits using direct ancestors only.
3595 * Also enter dependencies on the direct ancestors, and make sure they are
3596 * marked with relhassubclass = true.
3597 *
3598 * (Once upon a time, both direct and indirect ancestors were found here
3599 * and then entered into pg_ipl. Since that catalog doesn't exist
3600 * anymore, there's no need to look for indirect ancestors.)
3601 */
3603
3604 seqNumber = 1;
3605 foreach(entry, supers)
3606 {
3607 Oid parentOid = lfirst_oid(entry);
3608
3611 seqNumber++;
3612 }
3613
3614 table_close(relation, RowExclusiveLock);
3615}

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

3625{
3628
3629 /* store the pg_inherits row */
3631
3632 /*
3633 * Store a dependency too
3634 */
3636 parentobject.objectId = parentOid;
3637 parentobject.objectSubId = 0;
3639 childobject.objectId = relationId;
3640 childobject.objectSubId = 0;
3641
3644
3645 /*
3646 * Post creation hook of this inheritance. Since object_access_hook
3647 * doesn't take multiple object identifiers, we relay oid of parent
3648 * relation using auxiliary_id argument.
3649 */
3651 relationId, 0,
3652 parentOid, false);
3653
3654 /*
3655 * Mark the parent as having subclasses.
3656 */
3658}
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition tablecmds.c:3704

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

13585{
13586 ListCell *l;
13587 int attnum;
13588
13589 attnum = 0;
13590 foreach(l, colList)
13591 {
13592 char *attname = strVal(lfirst(l));
13595
13598 ereport(ERROR,
13600 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13601 attname)));
13603 if (attform->attnum < 0)
13604 ereport(ERROR,
13606 errmsg("system columns cannot be used in foreign keys")));
13607 if (attnum >= INDEX_MAX_KEYS)
13608 ereport(ERROR,
13610 errmsg("cannot have more than %d keys in a foreign key",
13611 INDEX_MAX_KEYS)));
13612 attnums[attnum] = attform->attnum;
13613 if (atttypids != NULL)
13614 atttypids[attnum] = attform->atttypid;
13615 if (attcollids != NULL)
13616 attcollids[attnum] = attform->attcollation;
13618 attnum++;
13619 }
13620
13621 return attnum;
13622}

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

13745{
13746 Oid indexoid = InvalidOid;
13747 bool found = false;
13748 bool found_deferrable = false;
13751 int i,
13752 j;
13753
13754 /*
13755 * Reject duplicate appearances of columns in the referenced-columns list.
13756 * Such a case is forbidden by the SQL standard, and even if we thought it
13757 * useful to allow it, there would be ambiguity about how to match the
13758 * list to unique indexes (in particular, it'd be unclear which index
13759 * opclass goes with which FK column).
13760 */
13761 for (i = 0; i < numattrs; i++)
13762 {
13763 for (j = i + 1; j < numattrs; j++)
13764 {
13765 if (attnums[i] == attnums[j])
13766 ereport(ERROR,
13768 errmsg("foreign key referenced-columns list must not contain duplicates")));
13769 }
13770 }
13771
13772 /*
13773 * Get the list of index OIDs for the table from the relcache, and look up
13774 * each one in the pg_index syscache, and match unique indexes to the list
13775 * of attnums we are given.
13776 */
13778
13779 foreach(indexoidscan, indexoidlist)
13780 {
13783
13784 indexoid = lfirst_oid(indexoidscan);
13787 elog(ERROR, "cache lookup failed for index %u", indexoid);
13789
13790 /*
13791 * Must have the right number of columns; must be unique (or if
13792 * temporal then exclusion instead) and not a partial index; forget it
13793 * if there are any expressions, too. Invalid indexes are out as well.
13794 */
13795 if (indexStruct->indnkeyatts == numattrs &&
13796 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
13800 {
13803
13804 /* Must get indclass the hard way */
13808
13809 /*
13810 * The given attnum list may match the index columns in any order.
13811 * Check for a match, and extract the appropriate opclasses while
13812 * we're at it.
13813 *
13814 * We know that attnums[] is duplicate-free per the test at the
13815 * start of this function, and we checked above that the number of
13816 * index columns agrees, so if we find a match for each attnums[]
13817 * entry then we must have a one-to-one match in some order.
13818 */
13819 for (i = 0; i < numattrs; i++)
13820 {
13821 found = false;
13822 for (j = 0; j < numattrs; j++)
13823 {
13824 if (attnums[i] == indexStruct->indkey.values[j])
13825 {
13826 opclasses[i] = indclass->values[j];
13827 found = true;
13828 break;
13829 }
13830 }
13831 if (!found)
13832 break;
13833 }
13834 /* The last attribute in the index must be the PERIOD FK part */
13835 if (found && with_period)
13836 {
13837 int16 periodattnum = attnums[numattrs - 1];
13838
13839 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
13840 }
13841
13842 /*
13843 * Refuse to use a deferrable unique/primary key. This is per SQL
13844 * spec, and there would be a lot of interesting semantic problems
13845 * if we tried to allow it.
13846 */
13847 if (found && !indexStruct->indimmediate)
13848 {
13849 /*
13850 * Remember that we found an otherwise matching index, so that
13851 * we can generate a more appropriate error message.
13852 */
13853 found_deferrable = true;
13854 found = false;
13855 }
13856
13857 /* We need to know whether the index has WITHOUT OVERLAPS */
13858 if (found)
13859 *pk_has_without_overlaps = indexStruct->indisexclusion;
13860 }
13862 if (found)
13863 break;
13864 }
13865
13866 if (!found)
13867 {
13868 if (found_deferrable)
13869 ereport(ERROR,
13871 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
13873 else
13874 ereport(ERROR,
13876 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
13878 }
13879
13881
13882 return indexoid;
13883}
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition heaptuple.c:456
Definition c.h:815

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

13642{
13649 int i;
13650
13651 /*
13652 * Get the list of index OIDs for the table from the relcache, and look up
13653 * each one in the pg_index syscache until we find one marked primary key
13654 * (hopefully there isn't more than one such). Insist it's valid, too.
13655 */
13656 *indexOid = InvalidOid;
13657
13659
13660 foreach(indexoidscan, indexoidlist)
13661 {
13662 Oid indexoid = lfirst_oid(indexoidscan);
13663
13666 elog(ERROR, "cache lookup failed for index %u", indexoid);
13668 if (indexStruct->indisprimary && indexStruct->indisvalid)
13669 {
13670 /*
13671 * Refuse to use a deferrable primary key. This is per SQL spec,
13672 * and there would be a lot of interesting semantic problems if we
13673 * tried to allow it.
13674 */
13675 if (!indexStruct->indimmediate)
13676 ereport(ERROR,
13678 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13680
13681 *indexOid = indexoid;
13682 break;
13683 }
13685 }
13686
13688
13689 /*
13690 * Check that we found it
13691 */
13692 if (!OidIsValid(*indexOid))
13693 ereport(ERROR,
13695 errmsg("there is no primary key for referenced table \"%s\"",
13697
13698 /* Must get indclass the hard way */
13702
13703 /*
13704 * Now build the list of PK attributes from the indkey definition (we
13705 * assume a primary key cannot have expressional elements)
13706 */
13707 *attnamelist = NIL;
13708 for (i = 0; i < indexStruct->indnkeyatts; i++)
13709 {
13710 int pkattno = indexStruct->indkey.values[i];
13711
13712 attnums[i] = pkattno;
13715 opclasses[i] = indclass->values[i];
13718 }
13719
13720 *pk_has_without_overlaps = indexStruct->indisexclusion;
13721
13723
13724 return i;
13725}
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 19980 of file tablecmds.c.

19981{
19983 ParseState *pstate;
19985 ListCell *l;
19986
19988
19989 newspec->strategy = partspec->strategy;
19990 newspec->partParams = NIL;
19991 newspec->location = partspec->location;
19992
19993 /* Check valid number of columns for strategy */
19994 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19995 list_length(partspec->partParams) != 1)
19996 ereport(ERROR,
19998 errmsg("cannot use \"list\" partition strategy with more than one column")));
19999
20000 /*
20001 * Create a dummy ParseState and insert the target relation as its sole
20002 * rangetable entry. We need a ParseState for transformExpr.
20003 */
20004 pstate = make_parsestate(NULL);
20006 NULL, false, true);
20007 addNSItemToQuery(pstate, nsitem, true, true, true);
20008
20009 /* take care of any partition expressions */
20010 foreach(l, partspec->partParams)
20011 {
20013
20014 if (pelem->expr)
20015 {
20016 /* Copy, to avoid scribbling on the input */
20018
20019 /* Now do parse transformation of the expression */
20020 pelem->expr = transformExpr(pstate, pelem->expr,
20022
20023 /* we have to fix its collations too */
20024 assign_expr_collations(pstate, pelem->expr);
20025 }
20026
20027 newspec->partParams = lappend(newspec->partParams, pelem);
20028 }
20029
20030 return newspec;
20031}
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition parse_expr.c:121
@ EXPR_KIND_PARTITION_EXPRESSION
Definition parse_node.h:81
@ PARTITION_STRATEGY_LIST
Definition parsenodes.h:917
List * partParams
Definition parsenodes.h:931
ParseLoc location
Definition parsenodes.h:932
PartitionStrategy strategy
Definition parsenodes.h:930

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

2497{
2498 /*
2499 * Don't allow truncate on temp tables of other backends ... their local
2500 * buffer manager is not going to cope.
2501 */
2502 if (RELATION_IS_OTHER_TEMP(rel))
2503 ereport(ERROR,
2505 errmsg("cannot truncate temporary tables of other sessions")));
2506
2507 /*
2508 * Also check for active uses of the relation in the current transaction,
2509 * including open scans and pending AFTER trigger events.
2510 */
2511 CheckTableNotInUse(rel, "TRUNCATE");
2512}

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

2479{
2480 char *relname = NameStr(reltuple->relname);
2482
2483 /* Permissions checks */
2485 if (aclresult != ACLCHECK_OK)
2487 relname);
2488}
#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 2427 of file tablecmds.c.

2428{
2429 char *relname = NameStr(reltuple->relname);
2430
2431 /*
2432 * Only allow truncate on regular tables, foreign tables using foreign
2433 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2434 * latter are only being included here for the following checks; no
2435 * physical truncation will occur in their case.).
2436 */
2437 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2438 {
2439 Oid serverid = GetForeignServerIdByRelId(relid);
2440 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2441
2442 if (!fdwroutine->ExecForeignTruncate)
2443 ereport(ERROR,
2445 errmsg("cannot truncate foreign table \"%s\"",
2446 relname)));
2447 }
2448 else if (reltuple->relkind != RELKIND_RELATION &&
2450 ereport(ERROR,
2452 errmsg("\"%s\" is not a table", relname)));
2453
2454 /*
2455 * Most system catalogs can't be truncated at all, or at least not unless
2456 * allow_system_table_mods=on. As an exception, however, we allow
2457 * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2458 * pg_upgrade, because we need to change its relfilenode to match the old
2459 * cluster, and allowing a TRUNCATE command to be executed is the easiest
2460 * way of doing that.
2461 */
2463 && (!IsBinaryUpgrade ||
2464 (relid != LargeObjectRelationId &&
2466 ereport(ERROR,
2468 errmsg("permission denied: \"%s\" is a system catalog",
2469 relname)));
2470
2472}
bool IsBinaryUpgrade
Definition globals.c:123
#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 11772 of file tablecmds.c.

11783{
11788
11792 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11794
11795 /*
11796 * Do some quick & easy initial checks. If any of these fail, we cannot
11797 * use this constraint.
11798 */
11799 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11800 {
11802 return false;
11803 }
11804 for (int i = 0; i < numfks; i++)
11805 {
11806 if (fk->conkey[i] != mapped_conkey[i] ||
11807 fk->confkey[i] != confkey[i] ||
11808 fk->conpfeqop[i] != conpfeqop[i])
11809 {
11811 return false;
11812 }
11813 }
11814
11815 /* Looks good so far; perform more extensive checks. */
11818 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11820
11821 /*
11822 * An error should be raised if the constraint enforceability is
11823 * different. Returning false without raising an error, as we do for other
11824 * attributes, could lead to a duplicate constraint with the same
11825 * enforceability as the parent. While this may be acceptable, it may not
11826 * be ideal. Therefore, it's better to raise an error and allow the user
11827 * to correct the enforceability before proceeding.
11828 */
11829 if (partConstr->conenforced != parentConstr->conenforced)
11830 ereport(ERROR,
11832 errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11833 NameStr(parentConstr->conname),
11834 NameStr(partConstr->conname),
11836
11837 if (OidIsValid(partConstr->conparentid) ||
11838 partConstr->condeferrable != parentConstr->condeferrable ||
11839 partConstr->condeferred != parentConstr->condeferred ||
11840 partConstr->confupdtype != parentConstr->confupdtype ||
11841 partConstr->confdeltype != parentConstr->confdeltype ||
11842 partConstr->confmatchtype != parentConstr->confmatchtype)
11843 {
11846 return false;
11847 }
11848
11851
11852 /* Looks good! Attach this constraint. */
11856
11857 return true;
11858}
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 16171 of file tablecmds.c.

16172{
16173 HeapTuple tup;
16174 Datum adatum;
16175 ArrayType *arr;
16176 Oid *rawarr;
16177 int numkeys;
16178 int i;
16179
16180 Assert(con->contype == CONSTR_FOREIGN);
16181 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
16182
16184 if (!HeapTupleIsValid(tup)) /* should not happen */
16185 elog(ERROR, "cache lookup failed for constraint %u", oldId);
16186
16189 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
16190 numkeys = ARR_DIMS(arr)[0];
16191 /* test follows the one in ri_FetchConstraintInfo() */
16192 if (ARR_NDIM(arr) != 1 ||
16193 ARR_HASNULL(arr) ||
16194 ARR_ELEMTYPE(arr) != OIDOID)
16195 elog(ERROR, "conpfeqop is not a 1-D Oid array");
16196 rawarr = (Oid *) ARR_DATA_PTR(arr);
16197
16198 /* stash a List of the operator Oids in our Constraint node */
16199 for (i = 0; i < numkeys; i++)
16201
16203}
#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 16142 of file tablecmds.c.

16143{
16145 stmt->accessMethod,
16146 stmt->indexParams,
16147 stmt->excludeOpNames,
16148 stmt->iswithoutoverlaps))
16149 {
16151
16152 /* If it's a partitioned index, there is no storage to share. */
16153 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
16154 {
16155 stmt->oldNumber = irel->rd_locator.relNumber;
16156 stmt->oldCreateSubid = irel->rd_createSubid;
16157 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
16158 }
16159 index_close(irel, NoLock);
16160 }
16161}
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition indexcmds.c:179

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

10727{
10728 int numcolsout = 0;
10729
10730 for (int i = 0; i < numfksetcols; i++)
10731 {
10733 bool seen = false;
10734
10735 /* Make sure it's in fkattnums[] */
10736 for (int j = 0; j < numfks; j++)
10737 {
10738 if (fkattnums[j] == setcol_attnum)
10739 {
10740 seen = true;
10741 break;
10742 }
10743 }
10744
10745 if (!seen)
10746 {
10747 char *col = strVal(list_nth(fksetcols, i));
10748
10749 ereport(ERROR,
10751 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10752 }
10753
10754 /* Now check for dups */
10755 seen = false;
10756 for (int j = 0; j < numcolsout; j++)
10757 {
10759 {
10760 seen = true;
10761 break;
10762 }
10763 }
10764 if (!seen)
10766 }
10767 return numcolsout;
10768}

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

13956{
13957 TupleTableSlot *slot;
13958 TableScanDesc scan;
13959 Trigger trig = {0};
13960 Snapshot snapshot;
13963
13965 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13966
13967 /*
13968 * Build a trigger call structure; we'll need it either way.
13969 */
13970 trig.tgoid = InvalidOid;
13971 trig.tgname = conname;
13972 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
13973 trig.tgisinternal = true;
13974 trig.tgconstrrelid = RelationGetRelid(pkrel);
13975 trig.tgconstrindid = pkindOid;
13976 trig.tgconstraint = constraintOid;
13977 trig.tgdeferrable = false;
13978 trig.tginitdeferred = false;
13979 /* we needn't fill in remaining fields */
13980
13981 /*
13982 * See if we can do it with a single LEFT JOIN query. A false result
13983 * indicates we must proceed with the fire-the-trigger method. We can't do
13984 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13985 * left joins.
13986 */
13987 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13988 return;
13989
13990 /*
13991 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13992 * if that tuple had just been inserted. If any of those fail, it should
13993 * ereport(ERROR) and that's that.
13994 */
13995 snapshot = RegisterSnapshot(GetLatestSnapshot());
13996 slot = table_slot_create(rel, NULL);
13997 scan = table_beginscan(rel, snapshot, 0, NULL,
13998 SO_NONE);
14000 "validateForeignKeyConstraint",
14003
14004 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
14005 {
14006 LOCAL_FCINFO(fcinfo, 0);
14007 TriggerData trigdata = {0};
14008
14010
14011 /*
14012 * Make a call to the trigger function
14013 *
14014 * No parameters are passed, but we do set a context
14015 */
14016 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
14017
14018 /*
14019 * We assume RI_FKey_check_ins won't look at flinfo...
14020 */
14021 trigdata.type = T_TriggerData;
14023 trigdata.tg_relation = rel;
14024 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
14025 trigdata.tg_trigslot = slot;
14026 trigdata.tg_trigger = &trig;
14027
14028 fcinfo->context = (Node *) &trigdata;
14029
14030 RI_FKey_check_ins(fcinfo);
14031
14033 }
14034
14037 table_endscan(scan);
14038 UnregisterSnapshot(snapshot);
14040}
#define MemSet(start, val, len)
Definition c.h:1107
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, SO_NONE, 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 22092 of file tablecmds.c.

22093{
22095 SysScanDesc scan;
22097 int tuples = 0;
22099 bool updated = false;
22100
22101 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
22102
22103 /*
22104 * Scan pg_inherits for this parent index. Count each valid index we find
22105 * (verifying the pg_index entry for each), and if we reach the total
22106 * amount we expect, we can mark this parent index as valid.
22107 */
22113 NULL, 1, &key);
22114 while ((inhTup = systable_getnext(scan)) != NULL)
22115 {
22119
22121 ObjectIdGetDatum(inhForm->inhrelid));
22123 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
22125 if (indexForm->indisvalid)
22126 tuples += 1;
22128 }
22129
22130 /* Done with pg_inherits */
22131 systable_endscan(scan);
22133
22134 /*
22135 * If we found as many inherited indexes as the partitioned table has
22136 * partitions, we're good; update pg_index to set indisvalid.
22137 */
22138 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
22139 {
22143
22148 elog(ERROR, "cache lookup failed for index %u",
22151
22152 indexForm->indisvalid = true;
22153 updated = true;
22154
22156
22159 }
22160
22161 /*
22162 * If this index is in turn a partition of a larger index, validating it
22163 * might cause the parent to become valid also. Try that.
22164 */
22165 if (updated && partedIdx->rd_rel->relispartition)
22166 {
22169 Relation parentIdx,
22170 parentTbl;
22171
22172 /* make sure we see the validation we just did */
22174
22179 Assert(!parentIdx->rd_index->indisvalid);
22180
22181 validatePartitionedIndex(parentIdx, parentTbl);
22182
22185 }
22186}

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

9656{
9658
9659 if (conForm->contype != CONSTRAINT_NOTNULL)
9660 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9661
9662 /* a NO INHERIT constraint is no good */
9663 if (conForm->connoinherit)
9664 ereport(ERROR,
9666 errmsg("cannot create primary key on column \"%s\"", colname),
9667 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9668 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9669 NameStr(conForm->conname), colname,
9670 get_rel_name(conForm->conrelid), "NO INHERIT"),
9671 errhint("You might need to make the existing constraint inheritable using %s.",
9672 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9673
9674 /* an unvalidated constraint is no good */
9675 if (!conForm->convalidated)
9676 ereport(ERROR,
9678 errmsg("cannot create primary key on column \"%s\"", colname),
9679 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9680 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9681 NameStr(conForm->conname), colname,
9682 get_rel_name(conForm->conrelid), "NOT VALID"),
9683 errhint("You might need to validate it using %s.",
9684 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9685}

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

22195{
22196 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
22197 {
22199 iinfo->ii_IndexAttrNumbers[i] - 1);
22200
22201 if (!att->attnotnull)
22202 ereport(ERROR,
22204 errmsg("invalid primary key definition"),
22205 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
22206 NameStr(att->attname),
22208 }
22209}

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

259 {
262 gettext_noop("table \"%s\" does not exist"),
263 gettext_noop("table \"%s\" does not exist, skipping"),
264 gettext_noop("\"%s\" is not a table"),
265 gettext_noop("Use DROP TABLE to remove a table.")},
268 gettext_noop("sequence \"%s\" does not exist"),
269 gettext_noop("sequence \"%s\" does not exist, skipping"),
270 gettext_noop("\"%s\" is not a sequence"),
271 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
274 gettext_noop("view \"%s\" does not exist"),
275 gettext_noop("view \"%s\" does not exist, skipping"),
276 gettext_noop("\"%s\" is not a view"),
277 gettext_noop("Use DROP VIEW to remove a view.")},
280 gettext_noop("materialized view \"%s\" does not exist"),
281 gettext_noop("materialized view \"%s\" does not exist, skipping"),
282 gettext_noop("\"%s\" is not a materialized view"),
283 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
286 gettext_noop("index \"%s\" does not exist"),
287 gettext_noop("index \"%s\" does not exist, skipping"),
288 gettext_noop("\"%s\" is not an index"),
289 gettext_noop("Use DROP INDEX to remove an index.")},
292 gettext_noop("type \"%s\" does not exist"),
293 gettext_noop("type \"%s\" does not exist, skipping"),
294 gettext_noop("\"%s\" is not a type"),
295 gettext_noop("Use DROP TYPE to remove a type.")},
298 gettext_noop("foreign table \"%s\" does not exist"),
299 gettext_noop("foreign table \"%s\" does not exist, skipping"),
300 gettext_noop("\"%s\" is not a foreign table"),
301 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
304 gettext_noop("table \"%s\" does not exist"),
305 gettext_noop("table \"%s\" does not exist, skipping"),
306 gettext_noop("\"%s\" is not a table"),
307 gettext_noop("Use DROP TABLE to remove a table.")},
310 gettext_noop("index \"%s\" does not exist"),
311 gettext_noop("index \"%s\" does not exist, skipping"),
312 gettext_noop("\"%s\" is not an index"),
313 gettext_noop("Use DROP INDEX to remove an index.")},
316 gettext_noop("property graph \"%s\" does not exist"),
317 gettext_noop("property graph \"%s\" does not exist, skipping"),
318 gettext_noop("\"%s\" is not a property graph"),
319 gettext_noop("Use DROP PROPERTY GRAPH to remove a property graph.")},
320 {'\0', 0, NULL, NULL, NULL, NULL}
321};
#define gettext_noop(x)
Definition c.h:1285

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits