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

7833{
7835 referenced;
7836
7837 /* We know the default collation is pinned, so don't bother recording it */
7839 {
7841 myself.objectId = relid;
7842 myself.objectSubId = attnum;
7844 referenced.objectId = collid;
7845 referenced.objectSubId = 0;
7847 }
7848}
#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:47
static int fb(int x)

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

Referenced by ATExecAddColumn(), and ATExecAlterColumnType().

◆ add_column_datatype_dependency()

static void add_column_datatype_dependency ( Oid  relid,
int32  attnum,
Oid  typid 
)
static

Definition at line 7814 of file tablecmds.c.

7815{
7817 referenced;
7818
7820 myself.objectId = relid;
7821 myself.objectSubId = attnum;
7822 referenced.classId = TypeRelationId;
7823 referenced.objectId = typid;
7824 referenced.objectSubId = 0;
7826}

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

10832{
10833 ObjectAddress address;
10834 Oid constrOid;
10835 char *conname;
10836 bool conislocal;
10838 bool connoinherit;
10839
10840 /*
10841 * Verify relkind for each referenced partition. At the top level, this
10842 * is redundant with a previous check, but we need it when recursing.
10843 */
10844 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10845 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10846 ereport(ERROR,
10848 errmsg("referenced relation \"%s\" is not a table",
10850
10851 /*
10852 * Caller supplies us with a constraint name; however, it may be used in
10853 * this partition, so come up with a different one in that case. Unless
10854 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10855 * supplied name with an underscore and digit(s) appended.
10856 */
10858 RelationGetRelid(rel),
10859 constraintname))
10860 conname = ChooseConstraintName(constraintname,
10861 NULL,
10862 "",
10864 else
10865 conname = constraintname;
10866
10867 if (fkconstraint->conname == NULL)
10868 fkconstraint->conname = pstrdup(conname);
10869
10871 {
10872 conislocal = false;
10873 coninhcount = 1;
10874 connoinherit = false;
10875 }
10876 else
10877 {
10878 conislocal = true;
10879 coninhcount = 0;
10880
10881 /*
10882 * always inherit for partitioned tables, never for legacy inheritance
10883 */
10885 }
10886
10887 /*
10888 * Record the FK constraint in pg_constraint.
10889 */
10893 fkconstraint->deferrable,
10894 fkconstraint->initdeferred,
10895 fkconstraint->is_enforced,
10896 fkconstraint->initially_valid,
10898 RelationGetRelid(rel),
10899 fkattnum,
10900 numfks,
10901 numfks,
10902 InvalidOid, /* not a domain constraint */
10903 indexOid,
10905 pkattnum,
10909 numfks,
10910 fkconstraint->fk_upd_action,
10911 fkconstraint->fk_del_action,
10914 fkconstraint->fk_matchtype,
10915 NULL, /* no exclusion constraint */
10916 NULL, /* no check constraint */
10917 NULL,
10918 conislocal, /* islocal */
10919 coninhcount, /* inhcount */
10920 connoinherit, /* conNoInherit */
10921 with_period, /* conPeriod */
10922 is_internal); /* is_internal */
10923
10925
10926 /*
10927 * In partitioning cases, create the dependency entries for this
10928 * constraint. (For non-partitioned cases, relevant entries were created
10929 * by CreateConstraintEntry.)
10930 *
10931 * On the referenced side, we need the constraint to have an internal
10932 * dependency on its parent constraint; this means that this constraint
10933 * cannot be dropped on its own -- only through the parent constraint. It
10934 * also means the containing partition cannot be dropped on its own, but
10935 * it can be detached, at which point this dependency is removed (after
10936 * verifying that no rows are referenced via this FK.)
10937 *
10938 * When processing the referencing side, we link the constraint via the
10939 * special partitioning dependencies: the parent constraint is the primary
10940 * dependent, and the partition on which the foreign key exists is the
10941 * secondary dependency. That way, this constraint is dropped if either
10942 * of these objects is.
10943 *
10944 * Note that this is only necessary for the subsidiary pg_constraint rows
10945 * in partitions; the topmost row doesn't need any of this.
10946 */
10948 {
10950
10952
10956 else
10957 {
10961 }
10962 }
10963
10964 /* make new constraint visible, in case we add more */
10966
10967 return address;
10968}
#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:1781
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 11003 of file tablecmds.c.

11012{
11015
11018
11019 /*
11020 * Create action triggers to enforce the constraint, or skip them if the
11021 * constraint is NOT ENFORCED.
11022 */
11023 if (fkconstraint->is_enforced)
11027 parentConstr, indexOid,
11030
11031 /*
11032 * If the referenced table is partitioned, recurse on ourselves to handle
11033 * each partition. We need one pg_constraint row created for each
11034 * partition in addition to the pg_constraint row for the parent table.
11035 */
11036 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11037 {
11039
11040 for (int i = 0; i < pd->nparts; i++)
11041 {
11042 Relation partRel;
11043 AttrMap *map;
11046 ObjectAddress address;
11047
11048 /* XXX would it be better to acquire these locks beforehand? */
11049 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
11050
11051 /*
11052 * Map the attribute numbers in the referenced side of the FK
11053 * definition to match the partition's column layout.
11054 */
11057 false);
11058 if (map)
11059 {
11061 for (int j = 0; j < numfks; j++)
11062 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
11063 }
11064 else
11066
11067 /* Determine the index to use at this level */
11068 partIndexId = index_get_partition(partRel, indexOid);
11069 if (!OidIsValid(partIndexId))
11070 elog(ERROR, "index for %u not found in partition %s",
11071 indexOid, RelationGetRelationName(partRel));
11072
11073 /* Create entry at this level ... */
11075 fkconstraint->conname, fkconstraint, rel,
11076 partRel, partIndexId, parentConstr,
11080 fkdelsetcols, true, with_period);
11081 /* ... and recurse to our children */
11083 partIndexId, address.objectId, numfks,
11089 with_period);
11090
11091 /* Done -- clean up (but keep the lock) */
11092 table_close(partRel, NoLock);
11093 if (map)
11094 {
11096 free_attrmap(map);
11097 }
11098 }
11099 }
11100}
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:76
int j
Definition isn.c:78
int i
Definition isn.c:77
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
Definition lmgr.c:334
#define NoLock
Definition lockdefs.h:34
#define ShareRowExclusiveLock
Definition lockdefs.h:41
void pfree(void *pointer)
Definition mcxt.c:1616
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition partdesc.c:71
Oid index_get_partition(Relation partition, Oid indexId)
Definition partition.c:176
#define RelationGetDescr(relation)
Definition rel.h: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 11141 of file tablecmds.c.

11149{
11152
11156
11157 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11158 ereport(ERROR,
11160 errmsg("foreign key constraints are not supported on foreign tables")));
11161
11162 /*
11163 * Add check triggers if the constraint is ENFORCED, and if needed,
11164 * schedule them to be checked in Phase 3.
11165 *
11166 * If the relation is partitioned, drill down to do it to its partitions.
11167 */
11168 if (fkconstraint->is_enforced)
11173 indexOid,
11176
11177 if (rel->rd_rel->relkind == RELKIND_RELATION)
11178 {
11179 /*
11180 * Tell Phase 3 to check that the constraint is satisfied by existing
11181 * rows. We can skip this during table creation, when constraint is
11182 * specified as NOT ENFORCED, or when requested explicitly by
11183 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11184 * recreating a constraint following a SET DATA TYPE operation that
11185 * did not impugn its validity.
11186 */
11187 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11188 fkconstraint->is_enforced)
11189 {
11191 AlteredTableInfo *tab;
11192
11193 tab = ATGetQueueEntry(wqueue, rel);
11194
11197 newcon->contype = CONSTR_FOREIGN;
11198 newcon->refrelid = RelationGetRelid(pkrel);
11199 newcon->refindid = indexOid;
11200 newcon->conid = parentConstr;
11201 newcon->conwithperiod = fkconstraint->fk_with_period;
11202 newcon->qual = (Node *) fkconstraint;
11203
11204 tab->constraints = lappend(tab->constraints, newcon);
11205 }
11206 }
11207 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11208 {
11211
11212 /*
11213 * Triggers of the foreign keys will be manipulated a bunch of times
11214 * in the loop below. To avoid repeatedly opening/closing the trigger
11215 * catalog relation, we open it here and pass it to the subroutines
11216 * called below.
11217 */
11219
11220 /*
11221 * Recurse to take appropriate action on each partition; either we
11222 * find an existing constraint to reparent to ours, or we create a new
11223 * one.
11224 */
11225 for (int i = 0; i < pd->nparts; i++)
11226 {
11227 Relation partition = table_open(pd->oids[i], lockmode);
11228 List *partFKs;
11229 AttrMap *attmap;
11231 bool attached;
11232 ObjectAddress address;
11233
11235
11237 RelationGetDescr(rel),
11238 false);
11239 for (int j = 0; j < numfks; j++)
11240 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11241
11242 /* Check whether an existing constraint can be repurposed */
11244 attached = false;
11246 {
11248 fk,
11249 partition,
11251 numfks,
11253 pkattnum,
11257 trigrel))
11258 {
11259 attached = true;
11260 break;
11261 }
11262 }
11263 if (attached)
11264 {
11266 continue;
11267 }
11268
11269 /*
11270 * No luck finding a good constraint to reuse; create our own.
11271 */
11273 fkconstraint->conname, fkconstraint,
11274 partition, pkrel, indexOid, parentConstr,
11279 with_period);
11280
11281 /* call ourselves to finalize the creation and we're done */
11283 indexOid,
11284 address.objectId,
11285 numfks,
11286 pkattnum,
11294 lockmode,
11297 with_period);
11298
11300 }
11301
11303 }
11304}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition attmap.c:175
#define palloc0_object(type)
Definition fe_memutils.h:75
List * lappend(List *list, void *datum)
Definition list.c:339
#define RowExclusiveLock
Definition lockdefs.h:38
char * get_constraint_name(Oid conoid)
Definition lsyscache.c:1200
#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:4722
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 12782 of file tablecmds.c.

12786{
12789 ScanKeyData skey[3];
12790
12791 ScanKeyInit(&skey[0],
12794 ObjectIdGetDatum(conrelid));
12795 ScanKeyInit(&skey[1],
12799 ScanKeyInit(&skey[2],
12802 CStringGetDatum(cmdcon->conname));
12803
12805 NULL, 3, skey);
12806
12808 ereport(ERROR,
12810 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12811 cmdcon->conname, get_rel_name(conrelid)));
12812
12814 recurse, recursing, lockmode);
12815
12817}
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:612
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:523
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:2121
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 13084 of file tablecmds.c.

13088{
13090 Oid conoid;
13091 ScanKeyData pkey;
13094
13096 conoid = currcon->oid;
13097
13098 ScanKeyInit(&pkey,
13101 ObjectIdGetDatum(conoid));
13102
13104 true, NULL, 1, &pkey);
13105
13107 {
13110
13111 childrel = table_open(childcon->conrelid, lockmode);
13112
13114 childtup, recurse, otherrelids, lockmode);
13116 }
13117
13119}
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 12966 of file tablecmds.c.

12969{
12973
12977 ObjectIdGetDatum(conoid));
12979 NULL, 1, &tgkey);
12981 {
12985
12986 /*
12987 * Remember OIDs of other relation(s) involved in FK constraint.
12988 * (Note: it's likely that we could skip forcing a relcache inval for
12989 * other rels that don't have a trigger whose properties change, but
12990 * let's be conservative.)
12991 */
12992 if (tgform->tgrelid != RelationGetRelid(rel))
12994 tgform->tgrelid);
12995
12996 /*
12997 * Update enable status and deferrability of RI_FKey_noaction_del,
12998 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12999 * triggers, but not others; see createForeignKeyActionTriggers and
13000 * CreateFKCheckTrigger.
13001 */
13002 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
13003 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
13004 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
13005 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
13006 continue;
13007
13010
13011 copy_tg->tgdeferrable = deferrable;
13012 copy_tg->tginitdeferred = initdeferred;
13014
13016
13018 }
13019
13021}
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 13126 of file tablecmds.c.

13128{
13131
13132 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
13133 cmdcon->alterInheritability);
13134
13137
13138 if (cmdcon->alterEnforceability)
13139 {
13140 copy_con->conenforced = cmdcon->is_enforced;
13141
13142 /*
13143 * NB: The convalidated status is irrelevant when the constraint is
13144 * set to NOT ENFORCED, but for consistency, it should still be set
13145 * appropriately. Similarly, if the constraint is later changed to
13146 * ENFORCED, validation will be performed during phase 3, so it makes
13147 * sense to mark it as valid in that case.
13148 */
13149 copy_con->convalidated = cmdcon->is_enforced;
13150 }
13151 if (cmdcon->alterDeferrability)
13152 {
13153 copy_con->condeferrable = cmdcon->deferrable;
13154 copy_con->condeferred = cmdcon->initdeferred;
13155 }
13156 if (cmdcon->alterInheritability)
13157 copy_con->connoinherit = cmdcon->noinherit;
13158
13161
13162 /* Make new constraint flags visible to others */
13164
13166}
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition inval.c:1691

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

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

13043{
13045 Oid conoid;
13046 ScanKeyData pkey;
13049
13051 conoid = currcon->oid;
13052
13053 ScanKeyInit(&pkey,
13056 ObjectIdGetDatum(conoid));
13057
13059 true, NULL, 1, &pkey);
13060
13063 pkrelid, childtup, lockmode,
13068
13070}
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 19395 of file tablecmds.c.

19397{
19398 List *indexList;
19399 ListCell *l;
19400
19402
19403 foreach(l, indexList)
19404 {
19405 Oid indexOid = lfirst_oid(l);
19407
19409 thisobj.objectId = indexOid;
19410 thisobj.objectSubId = 0;
19411
19412 /*
19413 * Note: currently, the index will not have its own dependency on the
19414 * namespace, so we don't need to do changeDependencyFor(). There's no
19415 * row type in pg_type, either.
19416 *
19417 * XXX this objsMoved test may be pointless -- surely we have a single
19418 * dependency link from a relation to each index?
19419 */
19421 {
19424 false, objsMoved);
19426 }
19427 }
19428
19430}
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:4827
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 19318 of file tablecmds.c.

19322{
19326 bool already_done = false;
19327
19328 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19331 elog(ERROR, "cache lookup failed for relation %u", relOid);
19333
19334 Assert(classForm->relnamespace == oldNspOid);
19335
19336 thisobj.classId = RelationRelationId;
19337 thisobj.objectId = relOid;
19338 thisobj.objectSubId = 0;
19339
19340 /*
19341 * If the object has already been moved, don't move it again. If it's
19342 * already in the right place, don't move it, but still fire the object
19343 * access hook.
19344 */
19346 if (!already_done && oldNspOid != newNspOid)
19347 {
19348 ItemPointerData otid = classTup->t_self;
19349
19350 /* check for duplicate name (more friendly than unique-index failure) */
19351 if (get_relname_relid(NameStr(classForm->relname),
19353 ereport(ERROR,
19355 errmsg("relation \"%s\" already exists in schema \"%s\"",
19356 NameStr(classForm->relname),
19358
19359 /* classTup is a copy, so OK to scribble on */
19360 classForm->relnamespace = newNspOid;
19361
19364
19365
19366 /* Update dependency on schema if caller said so */
19367 if (hasDependEntry &&
19369 relOid,
19371 oldNspOid,
19372 newNspOid) != 1)
19373 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19374 NameStr(classForm->relname));
19375 }
19376 else
19378 if (!already_done)
19379 {
19381
19383 }
19384
19386}
#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:3561
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition lsyscache.c:2078
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition pg_depend.c:459
HeapTuple SearchSysCacheLockedCopy1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c: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 19440 of file tablecmds.c.

19443{
19445 SysScanDesc scan;
19446 ScanKeyData key[2];
19447 HeapTuple tup;
19448
19449 /*
19450 * SERIAL sequences are those having an auto dependency on one of the
19451 * table's columns (we don't care *which* column, exactly).
19452 */
19454
19455 ScanKeyInit(&key[0],
19459 ScanKeyInit(&key[1],
19463 /* we leave refobjsubid unspecified */
19464
19466 NULL, 2, key);
19467
19468 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19469 {
19472
19473 /* skip dependencies other than auto dependencies on columns */
19474 if (depForm->refobjsubid == 0 ||
19475 depForm->classid != RelationRelationId ||
19476 depForm->objsubid != 0 ||
19477 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19478 continue;
19479
19480 /* Use relation_open just in case it's an index */
19481 seqRel = relation_open(depForm->objid, lockmode);
19482
19483 /* skip non-sequence relations */
19484 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19485 {
19486 /* No need to keep the lock */
19487 relation_close(seqRel, lockmode);
19488 continue;
19489 }
19490
19491 /* Fix the pg_class and pg_depend entries */
19494 true, objsMoved);
19495
19496 /*
19497 * Sequences used to have entries in pg_type, but no longer do. If we
19498 * ever re-instate that, we'll need to move the pg_type entry to the
19499 * new namespace, too (using AlterTypeNamespaceInternal).
19500 */
19502
19503 /* Now we can close it. Keep the lock till end of transaction. */
19505 }
19506
19507 systable_endscan(scan);
19508
19510}
@ 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 17258 of file tablecmds.c.

17259{
17260 List *relations = NIL;
17261 ListCell *l;
17262 ScanKeyData key[1];
17263 Relation rel;
17264 TableScanDesc scan;
17265 HeapTuple tuple;
17268 List *role_oids = roleSpecsToIds(stmt->roles);
17269
17270 /* Ensure we were not asked to move something we can't */
17271 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17272 stmt->objtype != OBJECT_MATVIEW)
17273 ereport(ERROR,
17275 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17276
17277 /* Get the orig and new tablespace OIDs */
17278 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17279 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17280
17281 /* Can't move shared relations in to or out of pg_global */
17282 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17285 ereport(ERROR,
17287 errmsg("cannot move relations in to or out of pg_global tablespace")));
17288
17289 /*
17290 * Must have CREATE rights on the new tablespace, unless it is the
17291 * database default tablespace (which all users implicitly have CREATE
17292 * rights on).
17293 */
17295 {
17297
17299 ACL_CREATE);
17300 if (aclresult != ACLCHECK_OK)
17303 }
17304
17305 /*
17306 * Now that the checks are done, check if we should set either to
17307 * InvalidOid because it is our database's default tablespace.
17308 */
17311
17314
17315 /* no-op */
17317 return new_tablespaceoid;
17318
17319 /*
17320 * Walk the list of objects in the tablespace and move them. This will
17321 * only find objects in our database, of course.
17322 */
17323 ScanKeyInit(&key[0],
17327
17329 scan = table_beginscan_catalog(rel, 1, key);
17330 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17331 {
17333 Oid relOid = relForm->oid;
17334
17335 /*
17336 * Do not move objects in pg_catalog as part of this, if an admin
17337 * really wishes to do so, they can issue the individual ALTER
17338 * commands directly.
17339 *
17340 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17341 * (TOAST will be moved with the main table).
17342 */
17343 if (IsCatalogNamespace(relForm->relnamespace) ||
17344 relForm->relisshared ||
17345 isAnyTempNamespace(relForm->relnamespace) ||
17346 IsToastNamespace(relForm->relnamespace))
17347 continue;
17348
17349 /* Only move the object type requested */
17350 if ((stmt->objtype == OBJECT_TABLE &&
17351 relForm->relkind != RELKIND_RELATION &&
17352 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17353 (stmt->objtype == OBJECT_INDEX &&
17354 relForm->relkind != RELKIND_INDEX &&
17355 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17356 (stmt->objtype == OBJECT_MATVIEW &&
17357 relForm->relkind != RELKIND_MATVIEW))
17358 continue;
17359
17360 /* Check if we are only moving objects owned by certain roles */
17361 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17362 continue;
17363
17364 /*
17365 * Handle permissions-checking here since we are locking the tables
17366 * and also to avoid doing a bunch of work only to fail part-way. Note
17367 * that permissions will also be checked by AlterTableInternal().
17368 *
17369 * Caller must be considered an owner on the table to move it.
17370 */
17373 NameStr(relForm->relname));
17374
17375 if (stmt->nowait &&
17377 ereport(ERROR,
17379 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17380 get_namespace_name(relForm->relnamespace),
17381 NameStr(relForm->relname))));
17382 else
17384
17385 /* Add to our list of objects to move */
17386 relations = lappend_oid(relations, relOid);
17387 }
17388
17389 table_endscan(scan);
17391
17392 if (relations == NIL)
17395 errmsg("no matching relations in tablespace \"%s\" found",
17396 orig_tablespaceoid == InvalidOid ? "(database default)" :
17398
17399 /* Everything is locked, loop through and move all of the relations. */
17400 foreach(l, relations)
17401 {
17402 List *cmds = NIL;
17404
17406 cmd->name = stmt->new_tablespacename;
17407
17408 cmds = lappend(cmds, cmd);
17409
17411 /* OID is set by AlterTableInternal */
17412 AlterTableInternal(lfirst_oid(l), cmds, false);
17414 }
17415
17416 return new_tablespaceoid;
17417}
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:3879
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4133
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:2196
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 19210 of file tablecmds.c.

19211{
19212 Relation rel;
19213 Oid relid;
19214 Oid oldNspOid;
19215 Oid nspOid;
19216 RangeVar *newrv;
19219
19221 stmt->missing_ok ? RVR_MISSING_OK : 0,
19223 stmt);
19224
19225 if (!OidIsValid(relid))
19226 {
19228 (errmsg("relation \"%s\" does not exist, skipping",
19229 stmt->relation->relname)));
19230 return InvalidObjectAddress;
19231 }
19232
19233 rel = relation_open(relid, NoLock);
19234
19236
19237 /* If it's an owned sequence, disallow moving it by itself. */
19238 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19239 {
19240 Oid tableId;
19241 int32 colId;
19242
19243 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19245 ereport(ERROR,
19247 errmsg("cannot move an owned sequence into another schema"),
19248 errdetail("Sequence \"%s\" is linked to table \"%s\".",
19251 }
19252
19253 /* Get and lock schema OID and check its permissions. */
19254 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19256
19257 /* common checks on switching namespaces */
19259
19263
19265
19266 if (oldschema)
19268
19269 /* close rel, but keep lock until commit */
19270 relation_close(rel, NoLock);
19271
19272 return myself;
19273}
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:901
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)

References AccessExclusiveLock, AlterTableNamespaceInternal(), CheckSetNamespace(), DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, ereport, errcode(), errdetail(), errmsg, ERROR, fb(), free_object_addresses(), get_rel_name(), InvalidObjectAddress, makeRangeVar(), new_object_addresses(), NoLock, NOTICE, ObjectAddressSet, OidIsValid, RangeVarCallbackForAlterRelation(), RangeVarGetAndCheckCreationNamespace(), RangeVarGetRelidExtended(), RelationData::rd_rel, relation_close(), relation_open(), RelationGetNamespace, RelationGetRelationName, RVR_MISSING_OK, sequenceIsOwned(), and stmt.

Referenced by ExecAlterObjectSchemaStmt().

◆ AlterTableNamespaceInternal()

void AlterTableNamespaceInternal ( Relation  rel,
Oid  oldNspOid,
Oid  nspOid,
ObjectAddresses objsMoved 
)

Definition at line 19281 of file tablecmds.c.

19283{
19285
19286 Assert(objsMoved != NULL);
19287
19288 /* OK, modify the pg_class row and pg_depend entry */
19290
19292 nspOid, true, objsMoved);
19293
19294 /* Fix the table's row type too, if it has one */
19295 if (OidIsValid(rel->rd_rel->reltype))
19297 false, /* isImplicitArray */
19298 false, /* ignoreDependent */
19299 false, /* errorOnTableType */
19300 objsMoved);
19301
19302 /* Fix other dependent stuff */
19307 false, objsMoved);
19308
19310}
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:4199

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

23192{
23193 Relation partRel;
23194 List *indexList;
23195
23196 if (extDepState == NIL)
23197 return;
23198
23199 /*
23200 * Use NoLock since the caller already holds AccessExclusiveLock on the
23201 * new partition.
23202 */
23203 partRel = table_open(newPartOid, NoLock);
23205
23206 foreach_oid(indexOid, indexList)
23207 {
23209
23210 if (!get_rel_relispartition(indexOid))
23211 continue;
23212
23213 parentIdxOid = get_partition_parent(indexOid, true);
23215 continue;
23216
23218 {
23220
23221 if (entry->parentIndexOid > parentIdxOid)
23222 break;
23223 if (entry->parentIndexOid < parentIdxOid)
23224 continue;
23225
23227
23228 foreach_oid(extOid, entry->extensionOids)
23229 {
23231
23235 }
23236 break;
23237 }
23238 }
23239
23241 table_close(partRel, NoLock);
23242}
@ DEPENDENCY_AUTO_EXTENSION
Definition dependency.h:39
bool get_rel_relispartition(Oid relid)
Definition lsyscache.c:2220
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 10015 of file tablecmds.c.

10018{
10019 List *newcons;
10020 ListCell *lcon;
10021 List *children;
10022 ListCell *child;
10024
10025 /* Guard against stack overflow due to overly deep inheritance tree. */
10027
10028 /* At top level, permission check was done in ATPrepCmd, else do it */
10029 if (recursing)
10032
10033 /*
10034 * Call AddRelationNewConstraints to do the work, making sure it works on
10035 * a copy of the Constraint so transformExpr can't modify the original. It
10036 * returns a list of cooked constraints.
10037 *
10038 * If the constraint ends up getting merged with a pre-existing one, it's
10039 * omitted from the returned list, which is what we want: we do not need
10040 * to do any validation work. That can only happen at child tables,
10041 * though, since we disallow merging at the top level.
10042 */
10044 list_make1(copyObject(constr)),
10045 recursing || is_readd, /* allow_merge */
10046 !recursing, /* is_local */
10047 is_readd, /* is_internal */
10048 NULL); /* queryString not available
10049 * here */
10050
10051 /* we don't expect more than one constraint here */
10052 Assert(list_length(newcons) <= 1);
10053
10054 /* Add each to-be-validated constraint to Phase 3's queue */
10055 foreach(lcon, newcons)
10056 {
10058
10059 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
10060 {
10062
10064 newcon->name = ccon->name;
10065 newcon->contype = ccon->contype;
10066 newcon->qual = ccon->expr;
10067
10068 tab->constraints = lappend(tab->constraints, newcon);
10069 }
10070
10071 /* Save the actually assigned name if it was defaulted */
10072 if (constr->conname == NULL)
10073 constr->conname = ccon->name;
10074
10075 /*
10076 * If adding a valid not-null constraint, set the pg_attribute flag
10077 * and tell phase 3 to verify existing rows, if needed. For an
10078 * invalid constraint, just set attnotnull, without queueing
10079 * verification.
10080 */
10081 if (constr->contype == CONSTR_NOTNULL)
10082 set_attnotnull(wqueue, rel, ccon->attnum,
10083 !constr->skip_validation,
10084 !constr->skip_validation);
10085
10086 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
10087 }
10088
10089 /* At this point we must have a locked-down name to use */
10090 Assert(newcons == NIL || constr->conname != NULL);
10091
10092 /* Advance command counter in case same table is visited multiple times */
10094
10095 /*
10096 * If the constraint got merged with an existing constraint, we're done.
10097 * We mustn't recurse to child tables in this case, because they've
10098 * already got the constraint, and visiting them again would lead to an
10099 * incorrect value for coninhcount.
10100 */
10101 if (newcons == NIL)
10102 return address;
10103
10104 /*
10105 * If adding a NO INHERIT constraint, no need to find our children.
10106 */
10107 if (constr->is_no_inherit)
10108 return address;
10109
10110 /*
10111 * Propagate to children as appropriate. Unlike most other ALTER
10112 * routines, we have to do this one level of recursion at a time; we can't
10113 * use find_all_inheritors to do it in one pass.
10114 */
10115 children =
10117
10118 /*
10119 * Check if ONLY was specified with ALTER TABLE. If so, allow the
10120 * constraint creation only if there are no children currently. Error out
10121 * otherwise.
10122 */
10123 if (!recurse && children != NIL)
10124 ereport(ERROR,
10126 errmsg("constraint must be added to child tables too")));
10127
10128 /*
10129 * Recurse to create the constraint on each child.
10130 */
10131 foreach(child, children)
10132 {
10133 Oid childrelid = lfirst_oid(child);
10136
10137 /* find_inheritance_children already got lock */
10140
10141 /* Find or create work queue entry for this table */
10143
10144 /* Recurse to this child */
10146 constr, recurse, true, is_readd, lockmode);
10147
10149 }
10150
10151 return address;
10152}
List * AddRelationNewConstraints(Relation rel, List *newColDefaults, List *newConstraints, bool allow_merge, bool is_local, bool is_internal, const char *queryString)
Definition heap.c:2399
@ 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:95
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)
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:7955

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

10173{
10186 bool with_period;
10188 int i;
10189 int numfks,
10190 numpks,
10192 Oid indexOid;
10193 bool old_check_ok;
10194 ObjectAddress address;
10196
10197 /*
10198 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10199 * delete rows out from under us.
10200 */
10201 if (OidIsValid(fkconstraint->old_pktable_oid))
10203 else
10205
10206 /*
10207 * Validity checks (permission checks wait till we have the column
10208 * numbers)
10209 */
10210 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10211 ereport(ERROR,
10213 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10216
10217 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10218 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10219 ereport(ERROR,
10221 errmsg("referenced relation \"%s\" is not a table",
10223
10225 ereport(ERROR,
10227 errmsg("permission denied: \"%s\" is a system catalog",
10229
10230 /*
10231 * References from permanent or unlogged tables to temp tables, and from
10232 * permanent tables to unlogged tables, are disallowed because the
10233 * referenced data can vanish out from under us. References from temp
10234 * tables to any other table type are also disallowed, because other
10235 * backends might need to run the RI triggers on the perm table, but they
10236 * can't reliably see tuples in the local buffers of other backends.
10237 */
10238 switch (rel->rd_rel->relpersistence)
10239 {
10242 ereport(ERROR,
10244 errmsg("constraints on permanent tables may reference only permanent tables")));
10245 break;
10248 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10249 ereport(ERROR,
10251 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10252 break;
10254 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10255 ereport(ERROR,
10257 errmsg("constraints on temporary tables may reference only temporary tables")));
10258 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10259 ereport(ERROR,
10261 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10262 break;
10263 }
10264
10265 /*
10266 * Look up the referencing attributes to make sure they exist, and record
10267 * their attnums and type and collation OIDs.
10268 */
10270 fkconstraint->fk_attrs,
10272 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10273 if (with_period && !fkconstraint->fk_with_period)
10274 ereport(ERROR,
10276 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10277
10279 fkconstraint->fk_del_set_cols,
10284 fkconstraint->fk_del_set_cols);
10285
10286 /*
10287 * If the attribute list for the referenced table was omitted, lookup the
10288 * definition of the primary key and use it. Otherwise, validate the
10289 * supplied attribute list. In either case, discover the index OID and
10290 * index opclasses, and the attnums and type and collation OIDs of the
10291 * attributes.
10292 */
10293 if (fkconstraint->pk_attrs == NIL)
10294 {
10296 &fkconstraint->pk_attrs,
10299
10300 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10301 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10302 ereport(ERROR,
10304 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10305 }
10306 else
10307 {
10309 fkconstraint->pk_attrs,
10311
10312 /* Since we got pk_attrs, one should be a period. */
10313 if (with_period && !fkconstraint->pk_with_period)
10314 ereport(ERROR,
10316 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10317
10318 /* Look for an index matching the column list */
10321 }
10322
10323 /*
10324 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10325 * must use PERIOD.
10326 */
10328 ereport(ERROR,
10330 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10331
10332 /*
10333 * Now we can check permissions.
10334 */
10336
10337 /*
10338 * Check some things for generated columns.
10339 */
10340 for (i = 0; i < numfks; i++)
10341 {
10342 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10343
10344 if (attgenerated)
10345 {
10346 /*
10347 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10348 */
10349 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10350 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10351 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10352 ereport(ERROR,
10354 errmsg("invalid %s action for foreign key constraint containing generated column",
10355 "ON UPDATE")));
10356 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10357 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10358 ereport(ERROR,
10360 errmsg("invalid %s action for foreign key constraint containing generated column",
10361 "ON DELETE")));
10362 }
10363
10364 /*
10365 * FKs on virtual columns are not supported. This would require
10366 * various additional support in ri_triggers.c, including special
10367 * handling in ri_NullCheck(), ri_KeysEqual(),
10368 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10369 * as NULL there). Also not really practical as long as you can't
10370 * index virtual columns.
10371 */
10372 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10373 ereport(ERROR,
10375 errmsg("foreign key constraints on virtual generated columns are not supported")));
10376 }
10377
10378 /*
10379 * Some actions are currently unsupported for foreign keys using PERIOD.
10380 */
10381 if (fkconstraint->fk_with_period)
10382 {
10383 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10384 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10385 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10386 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10387 ereport(ERROR,
10389 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10390 "ON UPDATE"));
10391
10392 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10393 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10394 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10395 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10396 ereport(ERROR,
10398 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10399 "ON DELETE"));
10400 }
10401
10402 /*
10403 * Look up the equality operators to use in the constraint.
10404 *
10405 * Note that we have to be careful about the difference between the actual
10406 * PK column type and the opclass' declared input type, which might be
10407 * only binary-compatible with it. The declared opcintype is the right
10408 * thing to probe pg_amop with.
10409 */
10410 if (numfks != numpks)
10411 ereport(ERROR,
10413 errmsg("number of referencing and referenced columns for foreign key disagree")));
10414
10415 /*
10416 * On the strength of a previous constraint, we might avoid scanning
10417 * tables to validate this one. See below.
10418 */
10419 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10420 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10421
10422 for (i = 0; i < numpks; i++)
10423 {
10424 Oid pktype = pktypoid[i];
10425 Oid fktype = fktypoid[i];
10426 Oid fktyped;
10427 Oid pkcoll = pkcolloid[i];
10428 Oid fkcoll = fkcolloid[i];
10431 Oid amid;
10432 Oid opfamily;
10433 Oid opcintype;
10434 bool for_overlaps;
10435 CompareType cmptype;
10436 Oid pfeqop;
10437 Oid ppeqop;
10438 Oid ffeqop;
10441
10442 /* We need several fields out of the pg_opclass entry */
10445 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10447 amid = cla_tup->opcmethod;
10448 opfamily = cla_tup->opcfamily;
10449 opcintype = cla_tup->opcintype;
10451
10452 /*
10453 * Get strategy number from index AM.
10454 *
10455 * For a normal foreign-key constraint, this should not fail, since we
10456 * already checked that the index is unique and should therefore have
10457 * appropriate equal operators. For a period foreign key, this could
10458 * fail if we selected a non-matching exclusion constraint earlier.
10459 * (XXX Maybe we should do these lookups earlier so we don't end up
10460 * doing that.)
10461 */
10462 for_overlaps = with_period && i == numpks - 1;
10464 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10466 ereport(ERROR,
10469 ? errmsg("could not identify an overlaps operator for foreign key")
10470 : errmsg("could not identify an equality operator for foreign key"),
10471 errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10472 cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10473
10474 /*
10475 * There had better be a primary equality operator for the index.
10476 * We'll use it for PK = PK comparisons.
10477 */
10478 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10479 eqstrategy);
10480
10481 if (!OidIsValid(ppeqop))
10482 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10483 eqstrategy, opcintype, opcintype, opfamily);
10484
10485 /*
10486 * Are there equality operators that take exactly the FK type? Assume
10487 * we should look through any domain here.
10488 */
10490
10491 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10492 eqstrategy);
10493 if (OidIsValid(pfeqop))
10494 {
10497 eqstrategy);
10498 }
10499 else
10500 {
10501 /* keep compiler quiet */
10504 }
10505
10506 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10507 {
10508 /*
10509 * Otherwise, look for an implicit cast from the FK type to the
10510 * opcintype, and if found, use the primary equality operator.
10511 * This is a bit tricky because opcintype might be a polymorphic
10512 * type such as ANYARRAY or ANYENUM; so what we have to test is
10513 * whether the two actual column types can be concurrently cast to
10514 * that type. (Otherwise, we'd fail to reject combinations such
10515 * as int[] and point[].)
10516 */
10517 Oid input_typeids[2];
10519
10520 input_typeids[0] = pktype;
10521 input_typeids[1] = fktype;
10522 target_typeids[0] = opcintype;
10523 target_typeids[1] = opcintype;
10526 {
10527 pfeqop = ffeqop = ppeqop;
10528 pfeqop_right = opcintype;
10529 }
10530 }
10531
10532 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10533 ereport(ERROR,
10535 errmsg("foreign key constraint \"%s\" cannot be implemented",
10536 fkconstraint->conname),
10537 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10538 "are of incompatible types: %s and %s.",
10539 strVal(list_nth(fkconstraint->fk_attrs, i)),
10540 strVal(list_nth(fkconstraint->pk_attrs, i)),
10543
10544 /*
10545 * This shouldn't be possible, but better check to make sure we have a
10546 * consistent state for the check below.
10547 */
10549 elog(ERROR, "key columns are not both collatable");
10550
10552 {
10553 bool pkcolldet;
10554 bool fkcolldet;
10555
10558
10559 /*
10560 * SQL requires that both collations are the same. This is
10561 * because we need a consistent notion of equality on both
10562 * columns. We relax this by allowing different collations if
10563 * they are both deterministic. (This is also for backward
10564 * compatibility, because PostgreSQL has always allowed this.)
10565 */
10566 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10567 ereport(ERROR,
10569 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10570 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10571 "have incompatible collations: \"%s\" and \"%s\". "
10572 "If either collation is nondeterministic, then both collations have to be the same.",
10573 strVal(list_nth(fkconstraint->fk_attrs, i)),
10574 strVal(list_nth(fkconstraint->pk_attrs, i)),
10577 }
10578
10579 if (old_check_ok)
10580 {
10581 /*
10582 * When a pfeqop changes, revalidate the constraint. We could
10583 * permit intra-opfamily changes, but that adds subtle complexity
10584 * without any concrete benefit for core types. We need not
10585 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10586 */
10588 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10590 }
10591 if (old_check_ok)
10592 {
10602 fkattnum[i] - 1);
10603
10604 /*
10605 * Identify coercion pathways from each of the old and new FK-side
10606 * column types to the right (foreign) operand type of the pfeqop.
10607 * We may assume that pg_constraint.conkey is not changing.
10608 */
10609 old_fktype = attr->atttypid;
10612 &old_castfunc);
10614 &new_castfunc);
10615
10616 old_fkcoll = attr->attcollation;
10618
10619 /*
10620 * Upon a change to the cast from the FK column to its pfeqop
10621 * operand, revalidate the constraint. For this evaluation, a
10622 * binary coercion cast is equivalent to no cast at all. While
10623 * type implementors should design implicit casts with an eye
10624 * toward consistency of operations like equality, we cannot
10625 * assume here that they have done so.
10626 *
10627 * A function with a polymorphic argument could change behavior
10628 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10629 * when the cast destination is polymorphic, we only avoid
10630 * revalidation if the input type has not changed at all. Given
10631 * just the core data types and operator classes, this requirement
10632 * prevents no would-be optimizations.
10633 *
10634 * If the cast converts from a base type to a domain thereon, then
10635 * that domain type must be the opcintype of the unique index.
10636 * Necessarily, the primary key column must then be of the domain
10637 * type. Since the constraint was previously valid, all values on
10638 * the foreign side necessarily exist on the primary side and in
10639 * turn conform to the domain. Consequently, we need not treat
10640 * domains specially here.
10641 *
10642 * If the collation changes, revalidation is required, unless both
10643 * collations are deterministic, because those share the same
10644 * notion of equality (because texteq reduces to bitwise
10645 * equality).
10646 *
10647 * We need not directly consider the PK type. It's necessarily
10648 * binary coercible to the opcintype of the unique index column,
10649 * and ri_triggers.c will only deal with PK datums in terms of
10650 * that opcintype. Changing the opcintype also changes pfeqop.
10651 */
10655 new_fktype == old_fktype) &&
10656 (new_fkcoll == old_fkcoll ||
10658 }
10659
10663 }
10664
10665 /*
10666 * For FKs with PERIOD we need additional operators to check whether the
10667 * referencing row's range is contained by the aggregated ranges of the
10668 * referenced row(s). For rangetypes and multirangetypes this is
10669 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10670 * support for now. FKs will look these up at "runtime", but we should
10671 * make sure the lookup works here, even if we don't use the values.
10672 */
10673 if (with_period)
10674 {
10678
10681 }
10682
10683 /* First, create the constraint catalog entry itself. */
10685 fkconstraint->conname, fkconstraint, rel, pkrel,
10686 indexOid,
10687 InvalidOid, /* no parent constraint */
10688 numfks,
10689 pkattnum,
10690 fkattnum,
10696 false,
10697 with_period);
10698
10699 /* Next process the action triggers at the referenced side and recurse */
10701 indexOid,
10702 address.objectId,
10703 numfks,
10704 pkattnum,
10705 fkattnum,
10713 with_period);
10714
10715 /* Lastly create the check triggers at the referencing side and recurse */
10717 indexOid,
10718 address.objectId,
10719 numfks,
10720 pkattnum,
10721 fkattnum,
10728 lockmode,
10730 with_period);
10731
10732 /*
10733 * Done. Close pk table, but keep lock until we've committed.
10734 */
10736
10737 return address;
10738}
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:1154
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:1173
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition lsyscache.c:1446
Oid getBaseType(Oid typid)
Definition lsyscache.c:2716
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 14951 of file tablecmds.c.

14952{
14953 Assert(expr != NULL);
14954
14955 for (;;)
14956 {
14957 /* only one varno, so no need to check that */
14958 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14959 return false;
14960 else if (IsA(expr, RelabelType))
14961 expr = (Node *) ((RelabelType *) expr)->arg;
14962 else if (IsA(expr, CoerceToDomain))
14963 {
14964 CoerceToDomain *d = (CoerceToDomain *) expr;
14965
14967 return true;
14968 expr = (Node *) d->arg;
14969 }
14970 else if (IsA(expr, FuncExpr))
14971 {
14972 FuncExpr *f = (FuncExpr *) expr;
14973
14974 switch (f->funcid)
14975 {
14979 return true;
14980 else
14981 expr = linitial(f->args);
14982 break;
14983 default:
14984 return true;
14985 }
14986 }
14987 else
14988 return true;
14989 }
14990}
bool TimestampTimestampTzRequiresRewrite(void)
Definition timestamp.c:6411
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:1495

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

22286{
22287 List *constraints;
22288 ListCell *cell;
22289
22290 constraints = GetParentedForeignKeyRefs(partition);
22291
22292 foreach(cell, constraints)
22293 {
22294 Oid constrOid = lfirst_oid(cell);
22295 HeapTuple tuple;
22297 Relation rel;
22298 Trigger trig = {0};
22299
22301 if (!HeapTupleIsValid(tuple))
22302 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22304
22305 Assert(OidIsValid(constrForm->conparentid));
22307
22308 /* prevent data changes into the referencing table until commit */
22309 rel = table_open(constrForm->conrelid, ShareLock);
22310
22311 trig.tgoid = InvalidOid;
22312 trig.tgname = NameStr(constrForm->conname);
22313 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
22314 trig.tgisinternal = true;
22315 trig.tgconstrrelid = RelationGetRelid(partition);
22316 trig.tgconstrindid = constrForm->conindid;
22317 trig.tgconstraint = constrForm->oid;
22318 trig.tgdeferrable = false;
22319 trig.tginitdeferred = false;
22320 /* we needn't fill in remaining fields */
22321
22323
22324 ReleaseSysCache(tuple);
22325
22326 table_close(rel, NoLock);
22327 }
22328}
#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 19723 of file tablecmds.c.

19725{
19727
19728 foreach(cur_item, on_commits)
19729 {
19731
19732 if (!isCommit && oc->creating_subid == mySubid)
19733 {
19734 /* cur_item must be removed */
19736 pfree(oc);
19737 }
19738 else
19739 {
19740 /* cur_item must be preserved */
19741 if (oc->creating_subid == mySubid)
19742 oc->creating_subid = parentSubid;
19743 if (oc->deleting_subid == mySubid)
19744 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19745 }
19746 }
19747}
#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 19691 of file tablecmds.c.

19692{
19694
19695 foreach(cur_item, on_commits)
19696 {
19698
19699 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19700 oc->creating_subid != InvalidSubTransactionId)
19701 {
19702 /* cur_item must be removed */
19704 pfree(oc);
19705 }
19706 else
19707 {
19708 /* cur_item must be preserved */
19709 oc->creating_subid = InvalidSubTransactionId;
19710 oc->deleting_subid = InvalidSubTransactionId;
19711 }
19712 }
19713}

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 * Note: we use build_column_default, and not just the cooked default
7533 * returned by AddRelationNewConstraints, so that the right thing happens
7534 * when a datatype's default applies.
7535 *
7536 * Note: it might seem that this should happen at the end of Phase 2, so
7537 * that the effects of subsequent subcommands can be taken into account.
7538 * It's intentional that we do it now, though. The new column should be
7539 * filled according to what is said in the ADD COLUMN subcommand, so that
7540 * the effects are the same as if this subcommand had been run by itself
7541 * and the later subcommands had been issued in new ALTER TABLE commands.
7542 *
7543 * We can skip this entirely for relations without storage, since Phase 3
7544 * is certainly not going to touch them.
7545 */
7546 if (RELKIND_HAS_STORAGE(relkind))
7547 {
7549 bool has_missing = false;
7550 bool has_volatile = false;
7551
7552 /*
7553 * For an identity column, we can't use build_column_default(),
7554 * because the sequence ownership isn't set yet. So do it manually.
7555 */
7556 if (colDef->identity)
7557 {
7559
7560 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7561 nve->typeId = attribute->atttypid;
7562
7563 defval = (Expr *) nve;
7564 }
7565 else
7566 defval = (Expr *) build_column_default(rel, attribute->attnum);
7567
7570
7571 /*
7572 * If the domain has volatile constraints, we must do a table rewrite
7573 * since the constraint result could differ per row and cannot be
7574 * evaluated once and cached as a missing value.
7575 */
7576 if (has_volatile)
7578
7579 /* Build CoerceToDomain(NULL) expression if needed */
7580 if (!defval && has_domain_constraints)
7581 {
7585
7586 baseTypeMod = attribute->atttypmod;
7590 defval = (Expr *) coerce_to_target_type(NULL,
7591 (Node *) defval,
7592 baseTypeId,
7593 attribute->atttypid,
7594 attribute->atttypmod,
7597 -1);
7598 if (defval == NULL) /* should not happen */
7599 elog(ERROR, "failed to coerce base type to domain");
7600 }
7601
7602 if (defval)
7603 {
7605
7606 /* Prepare defval for execution, either here or in Phase 3 */
7607 defval = expression_planner(defval);
7608
7609 /* Add the new default to the newvals list */
7611 newval->attnum = attribute->attnum;
7612 newval->expr = defval;
7613 newval->is_generated = (colDef->generated != '\0');
7614
7615 tab->newvals = lappend(tab->newvals, newval);
7616
7617 /*
7618 * Attempt to skip a complete table rewrite by storing the
7619 * specified DEFAULT value outside of the heap. This is only
7620 * allowed for plain relations and non-generated columns, and the
7621 * default expression can't be volatile (stable is OK), and the
7622 * domain constraint expressions can't be volatile (stable is OK).
7623 *
7624 * Note that contain_volatile_functions considers CoerceToDomain
7625 * immutable, so we rely on DomainHasConstraints (called above)
7626 * rather than checking defval alone.
7627 *
7628 * For domains with non-volatile constraints, we evaluate the
7629 * default using soft error handling: if the constraint check
7630 * fails (e.g., CHECK(value > 10) with DEFAULT 8), we fall back to
7631 * a table rewrite. This preserves the historical behavior that
7632 * such a failure is only raised when the table has rows.
7633 */
7634 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7635 !colDef->generated &&
7636 !has_volatile &&
7637 !contain_volatile_functions((Node *) defval))
7638 {
7639 EState *estate;
7642 bool missingIsNull;
7644
7645 /* Evaluate the default expression with soft errors */
7646 estate = CreateExecutorState();
7647 exprState = ExecPrepareExprWithContext(defval, estate,
7648 (Node *) &escontext);
7650 GetPerTupleExprContext(estate),
7651 &missingIsNull);
7652
7653 /*
7654 * If the domain constraint check failed (via errsave),
7655 * missingval is unreliable. Fall back to a table rewrite;
7656 * Phase 3 will re-evaluate with hard errors, so the user gets
7657 * an error only if the table has rows.
7658 */
7659 if (escontext.error_occurred)
7660 {
7661 missingIsNull = true;
7663 }
7664
7665 /* If it turns out NULL, nothing to do; else store it */
7666 if (!missingIsNull)
7667 {
7669 /* Make the additional catalog change visible */
7671 has_missing = true;
7672 }
7673 FreeExecutorState(estate);
7674 }
7675 else
7676 {
7677 /*
7678 * Failed to use missing mode. We have to do a table rewrite
7679 * to install the value --- unless it's a virtual generated
7680 * column.
7681 */
7682 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7684 }
7685 }
7686
7687 if (!has_missing)
7688 {
7689 /*
7690 * If the new column is NOT NULL, and there is no missing value,
7691 * tell Phase 3 it needs to check for NULLs.
7692 */
7693 tab->verify_new_notnull |= colDef->is_not_null;
7694 }
7695 }
7696
7697 /*
7698 * Add needed dependency entries for the new column.
7699 */
7702
7703 /*
7704 * Propagate to children as appropriate. Unlike most other ALTER
7705 * routines, we have to do this one level of recursion at a time; we can't
7706 * use find_all_inheritors to do it in one pass.
7707 */
7708 children =
7710
7711 /*
7712 * If we are told not to recurse, there had better not be any child
7713 * tables; else the addition would put them out of step.
7714 */
7715 if (children && !recurse)
7716 ereport(ERROR,
7718 errmsg("column must be added to child tables too")));
7719
7720 /* Children should see column as singly inherited */
7721 if (!recursing)
7722 {
7723 childcmd = copyObject(*cmd);
7725 colDef->inhcount = 1;
7726 colDef->is_local = false;
7727 }
7728 else
7729 childcmd = *cmd; /* no need to copy again */
7730
7731 foreach(child, children)
7732 {
7733 Oid childrelid = lfirst_oid(child);
7736
7737 /* find_inheritance_children already got lock */
7740
7741 /* Find or create work queue entry for this table */
7743
7744 /* Recurse to child; return value is ignored */
7746 &childcmd, recurse, true,
7747 lockmode, cur_pass, context);
7748
7750 }
7751
7753 return address;
7754}
bool contain_volatile_functions(Node *clause)
Definition clauses.c:551
#define AT_REWRITE_DEFAULT_VAL
ExprState * ExecPrepareExprWithContext(Expr *node, EState *estate, Node *escontext)
Definition execExpr.c:798
void FreeExecutorState(EState *estate)
Definition execUtils.c:197
EState * CreateExecutorState(void)
Definition execUtils.c:90
#define GetPerTupleExprContext(estate)
Definition executor.h:667
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:403
#define palloc_object(type)
Definition fe_memutils.h:74
#define newval
void CheckAttributeType(const char *attname, Oid atttypid, Oid attcollation, List *containing_rowtypes, int flags)
Definition heap.c:548
void InsertPgAttributeTuples(Relation pg_attribute_rel, TupleDesc tupdesc, Oid new_rel_oid, const FormExtraData_pg_attribute tupdesc_extra[], CatalogIndexState indstate)
Definition heap.c:731
void StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
Definition heap.c:2044
#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:3251
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition lsyscache.c:2733
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:7027
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:7814
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition tablecmds.c:7832
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:7761
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, ErrorSaveContext::error_occurred, ExecEvalExpr(), ExecPrepareExprWithContext(), 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 9899 of file tablecmds.c.

9902{
9904
9906
9907 /*
9908 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9909 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9910 * parse_utilcmd.c).
9911 */
9912 switch (newConstraint->contype)
9913 {
9914 case CONSTR_CHECK:
9915 case CONSTR_NOTNULL:
9916 address =
9918 newConstraint, recurse, false, is_readd,
9919 lockmode);
9920 break;
9921
9922 case CONSTR_FOREIGN:
9923
9924 /*
9925 * Assign or validate constraint name
9926 */
9927 if (newConstraint->conname)
9928 {
9930 RelationGetRelid(rel),
9931 newConstraint->conname))
9932 ereport(ERROR,
9934 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9935 newConstraint->conname,
9937 }
9938 else
9939 newConstraint->conname =
9942 "fkey",
9944 NIL);
9945
9946 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9948 recurse, false,
9949 lockmode);
9950 break;
9951
9952 default:
9953 elog(ERROR, "unrecognized constraint type: %d",
9954 (int) newConstraint->contype);
9955 }
9956
9957 return address;
9958}
@ CONSTR_CHECK
#define ERRCODE_DUPLICATE_OBJECT
Definition streamutil.c:30
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
Definition tablecmds.c:9973
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 8355 of file tablecmds.c.

8357{
8359 HeapTuple tuple;
8362 ObjectAddress address;
8364 bool ispartitioned;
8365
8366 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8367 if (ispartitioned && !recurse)
8368 ereport(ERROR,
8370 errmsg("cannot add identity to a column of only the partitioned table"),
8371 errhint("Do not specify the ONLY keyword.")));
8372
8373 if (rel->rd_rel->relispartition && !recursing)
8374 ereport(ERROR,
8376 errmsg("cannot add identity to a column of a partition"));
8377
8379
8381 if (!HeapTupleIsValid(tuple))
8382 ereport(ERROR,
8384 errmsg("column \"%s\" of relation \"%s\" does not exist",
8387 attnum = attTup->attnum;
8388
8389 /* Can't alter a system attribute */
8390 if (attnum <= 0)
8391 ereport(ERROR,
8393 errmsg("cannot alter system column \"%s\"",
8394 colName)));
8395
8396 /*
8397 * Creating a column as identity implies NOT NULL, so adding the identity
8398 * to an existing column that is not NOT NULL would create a state that
8399 * cannot be reproduced without contortions.
8400 */
8401 if (!attTup->attnotnull)
8402 ereport(ERROR,
8404 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8406
8407 /*
8408 * On the other hand, if a not-null constraint exists, then verify that
8409 * it's compatible.
8410 */
8411 if (attTup->attnotnull)
8412 {
8415
8417 attnum);
8419 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8421
8423 if (!conForm->convalidated)
8424 ereport(ERROR,
8426 errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8427 NameStr(conForm->conname), RelationGetRelationName(rel)),
8428 errhint("You might need to validate it using %s.",
8429 "ALTER TABLE ... VALIDATE CONSTRAINT"));
8430 }
8431
8432 if (attTup->attidentity)
8433 ereport(ERROR,
8435 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8437
8438 if (attTup->atthasdef)
8439 ereport(ERROR,
8441 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8443
8444 attTup->attidentity = cdef->identity;
8445 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8446
8448 RelationGetRelid(rel),
8449 attTup->attnum);
8451 RelationGetRelid(rel), attnum);
8452 heap_freetuple(tuple);
8453
8455
8456 /*
8457 * Recurse to propagate the identity column to partitions. Identity is
8458 * not inherited in regular inheritance children.
8459 */
8460 if (recurse && ispartitioned)
8461 {
8462 List *children;
8463 ListCell *lc;
8464
8465 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8466
8467 foreach(lc, children)
8468 {
8470
8472 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8474 }
8475 }
8476
8477 return address;
8478}
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:8355

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

9724{
9725 bool check_rights;
9726 bool skip_build;
9727 bool quiet;
9728 ObjectAddress address;
9729
9731 Assert(!stmt->concurrent);
9732
9733 /* The IndexStmt has already been through transformIndexStmt */
9734 Assert(stmt->transformed);
9735
9736 /* suppress schema rights check when rebuilding existing index */
9738 /* skip index build if phase 3 will do it or we're reusing an old one */
9739 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9740 /* suppress notices when rebuilding existing index */
9741 quiet = is_rebuild;
9742
9743 address = DefineIndex(NULL,
9744 RelationGetRelid(rel),
9745 stmt,
9746 InvalidOid, /* no predefined OID */
9747 InvalidOid, /* no parent index */
9748 InvalidOid, /* no parent constraint */
9749 -1, /* total_parts unknown */
9750 true, /* is_alter_table */
9752 false, /* check_not_in_use - we did it already */
9753 skip_build,
9754 quiet);
9755
9756 /*
9757 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9758 * new index instead of building from scratch. Restore associated fields.
9759 * This may store InvalidSubTransactionId in both fields, in which case
9760 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9761 * this after the CCI that made catalog rows visible to any rebuild. The
9762 * DROP of the old edition of this index will have scheduled the storage
9763 * for deletion at commit, so cancel that pending deletion.
9764 */
9765 if (RelFileNumberIsValid(stmt->oldNumber))
9766 {
9767 Relation irel = index_open(address.objectId, NoLock);
9768
9769 irel->rd_createSubid = stmt->oldCreateSubid;
9770 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9772 index_close(irel, NoLock);
9773 }
9774
9775 return address;
9776}
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 9807 of file tablecmds.c.

9809{
9810 Oid index_oid = stmt->indexOid;
9811 Relation indexRel;
9812 char *indexName;
9813 IndexInfo *indexInfo;
9814 char *constraintName;
9815 char constraintType;
9816 ObjectAddress address;
9817 uint16 flags;
9818
9821 Assert(stmt->isconstraint);
9822
9823 /*
9824 * Doing this on partitioned tables is not a simple feature to implement,
9825 * so let's punt for now.
9826 */
9827 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9828 ereport(ERROR,
9830 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9831
9833
9835
9836 indexInfo = BuildIndexInfo(indexRel);
9837
9838 /* this should have been checked at parse time */
9839 if (!indexInfo->ii_Unique)
9840 elog(ERROR, "index \"%s\" is not unique", indexName);
9841
9842 /*
9843 * Determine name to assign to constraint. We require a constraint to
9844 * have the same name as the underlying index; therefore, use the index's
9845 * existing name as the default constraint name, and if the user
9846 * explicitly gives some other name for the constraint, rename the index
9847 * to match.
9848 */
9849 constraintName = stmt->idxname;
9850 if (constraintName == NULL)
9852 else if (strcmp(constraintName, indexName) != 0)
9853 {
9855 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9858 }
9859
9860 /* Extra checks needed if making primary key */
9861 if (stmt->primary)
9862 index_check_primary_key(rel, indexInfo, true, stmt);
9863
9864 /* Note we currently don't support EXCLUSION constraints here */
9865 if (stmt->primary)
9867 else
9869
9870 /* Create the catalog entries for the constraint */
9873 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9874 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9876
9877 address = index_constraint_create(rel,
9878 index_oid,
9879 InvalidOid,
9880 indexInfo,
9883 flags,
9885 false); /* is_internal */
9886
9887 index_close(indexRel, NoLock);
9888
9889 return address;
9890}
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 17529 of file tablecmds.c.

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

18481{
18482 Oid relid = RelationGetRelid(rel);
18485 Oid typeid;
18488 SysScanDesc scan;
18491 type_attno;
18495 typeobj;
18497
18498 /* Validate the type. */
18499 typetuple = typenameType(NULL, ofTypename, NULL);
18502 typeid = typeform->oid;
18503
18504 /* Fail if the table has any inheritance parents. */
18506 ScanKeyInit(&key,
18509 ObjectIdGetDatum(relid));
18511 true, NULL, 1, &key);
18513 ereport(ERROR,
18515 errmsg("typed tables cannot inherit")));
18516 systable_endscan(scan);
18518
18519 /*
18520 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18521 * require that the order also match. However, attnotnull need not match.
18522 */
18525 table_attno = 1;
18526 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18527 {
18529 table_attr;
18530 const char *type_attname,
18532
18533 /* Get the next non-dropped type attribute. */
18535 if (type_attr->attisdropped)
18536 continue;
18537 type_attname = NameStr(type_attr->attname);
18538
18539 /* Get the next non-dropped table attribute. */
18540 do
18541 {
18542 if (table_attno > tableTupleDesc->natts)
18543 ereport(ERROR,
18545 errmsg("table is missing column \"%s\"",
18546 type_attname)));
18548 table_attno++;
18549 } while (table_attr->attisdropped);
18550 table_attname = NameStr(table_attr->attname);
18551
18552 /* Compare name. */
18554 ereport(ERROR,
18556 errmsg("table has column \"%s\" where type requires \"%s\"",
18558
18559 /* Compare type. */
18560 if (table_attr->atttypid != type_attr->atttypid ||
18561 table_attr->atttypmod != type_attr->atttypmod ||
18562 table_attr->attcollation != type_attr->attcollation)
18563 ereport(ERROR,
18565 errmsg("table \"%s\" has different type for column \"%s\"",
18567 }
18569
18570 /* Any remaining columns at the end of the table had better be dropped. */
18571 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18572 {
18574 table_attno - 1);
18575
18576 if (!table_attr->attisdropped)
18577 ereport(ERROR,
18579 errmsg("table has extra column \"%s\"",
18580 NameStr(table_attr->attname))));
18581 }
18582
18583 /* If the table was already typed, drop the existing dependency. */
18584 if (rel->rd_rel->reloftype)
18585 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18587
18588 /* Record a dependency on the new type. */
18589 tableobj.classId = RelationRelationId;
18590 tableobj.objectId = relid;
18591 tableobj.objectSubId = 0;
18592 typeobj.classId = TypeRelationId;
18593 typeobj.objectId = typeid;
18594 typeobj.objectSubId = 0;
18596
18597 /* Update pg_class.reloftype */
18601 elog(ERROR, "cache lookup failed for relation %u", relid);
18602 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18604
18606
18609
18611
18612 return typeobj;
18613}
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:1947

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

9788{
9789 ObjectAddress address;
9790
9792
9793 /* The CreateStatsStmt has already been through transformStatsStmt */
9794 Assert(stmt->transformed);
9795
9796 address = CreateStatistics(stmt, !is_rebuild);
9797
9798 return address;
9799}
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 12666 of file tablecmds.c.

12669{
12671 Relation rel;
12672 bool changed = false;
12673 List *children = NIL;
12674
12675 /* Since this function recurses, it could be driven to stack overflow */
12677
12678 Assert(cmdcon->alterEnforceability);
12679
12681
12682 Assert(currcon->contype == CONSTRAINT_CHECK);
12683
12684 /*
12685 * Parent relation already locked by caller, children will be locked by
12686 * find_all_inheritors. So NoLock is fine here.
12687 */
12688 rel = table_open(currcon->conrelid, NoLock);
12689
12690 if (currcon->conenforced != cmdcon->is_enforced)
12691 {
12693 changed = true;
12694 }
12695
12696 /*
12697 * Note that we must recurse even when trying to change a check constraint
12698 * to not enforced if it is already not enforced, in case descendant
12699 * constraints might be enforced and need to be changed to not enforced.
12700 * Conversely, we should do nothing if a constraint is being set to
12701 * enforced and is already enforced, as descendant constraints cannot be
12702 * different in that case.
12703 */
12704 if (!cmdcon->is_enforced || changed)
12705 {
12706 /*
12707 * If we're recursing, the parent has already done this, so skip it.
12708 * Also, if the constraint is a NO INHERIT constraint, we shouldn't
12709 * try to look for it in the children.
12710 */
12711 if (!recursing && !currcon->connoinherit)
12712 children = find_all_inheritors(RelationGetRelid(rel),
12713 lockmode, NULL);
12714
12715 foreach_oid(childoid, children)
12716 {
12717 if (childoid == RelationGetRelid(rel))
12718 continue;
12719
12720 /*
12721 * If we are told not to recurse, there had better not be any
12722 * child tables, because we can't change constraint enforceability
12723 * on the parent unless we have changed enforceability for all
12724 * child.
12725 */
12726 if (!recurse)
12727 ereport(ERROR,
12729 errmsg("constraint must be altered on child tables too"),
12730 errhint("Do not specify the ONLY keyword."));
12731
12733 childoid, false, true,
12734 lockmode);
12735 }
12736 }
12737
12738 /*
12739 * Tell Phase 3 to check that the constraint is satisfied by existing
12740 * rows. We only need do this when altering the constraint from NOT
12741 * ENFORCED to ENFORCED.
12742 */
12743 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12744 !currcon->conenforced &&
12745 cmdcon->is_enforced)
12746 {
12747 AlteredTableInfo *tab;
12749 Datum val;
12750 char *conbin;
12751
12753 newcon->name = pstrdup(NameStr(currcon->conname));
12754 newcon->contype = CONSTR_CHECK;
12755
12760
12761 /* Find or create work queue entry for this table */
12762 tab = ATGetQueueEntry(wqueue, rel);
12763 tab->constraints = lappend(tab->constraints, newcon);
12764 }
12765
12766 table_close(rel, NoLock);
12767
12768 return changed;
12769}
#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 16227 of file tablecmds.c.

16231{
16234 ForeignServer *server;
16236 HeapTuple tuple;
16237 HeapTuple newtuple;
16238 bool isnull;
16242 Datum datum;
16246 ObjectAddress address;
16247
16248 if (options == NIL)
16249 return InvalidObjectAddress;
16250
16251 /* First, determine FDW validator associated to the foreign table. */
16254 if (!HeapTupleIsValid(tuple))
16255 ereport(ERROR,
16257 errmsg("foreign table \"%s\" does not exist",
16260 server = GetForeignServer(fttableform->ftserver);
16261 fdw = GetForeignDataWrapper(server->fdwid);
16262
16264 ReleaseSysCache(tuple);
16265
16268 if (!HeapTupleIsValid(tuple))
16269 ereport(ERROR,
16271 errmsg("column \"%s\" of relation \"%s\" does not exist",
16273
16274 /* Prevent them from altering a system attribute */
16276 attnum = atttableform->attnum;
16277 if (attnum <= 0)
16278 ereport(ERROR,
16280 errmsg("cannot alter system column \"%s\"", colName)));
16281
16282
16283 /* Initialize buffers for new tuple values */
16284 memset(repl_val, 0, sizeof(repl_val));
16285 memset(repl_null, false, sizeof(repl_null));
16286 memset(repl_repl, false, sizeof(repl_repl));
16287
16288 /* Extract the current options */
16289 datum = SysCacheGetAttr(ATTNAME,
16290 tuple,
16292 &isnull);
16293 if (isnull)
16294 datum = PointerGetDatum(NULL);
16295
16296 /* Transform the options */
16298 datum,
16299 options,
16300 fdw->fdwvalidator);
16301
16302 if (DatumGetPointer(datum) != NULL)
16304 else
16306
16308
16309 /* Everything looks good - update the tuple */
16310
16311 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16313
16314 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16315
16317 RelationGetRelid(rel),
16318 atttableform->attnum);
16320 RelationGetRelid(rel), attnum);
16321
16322 ReleaseSysCache(tuple);
16323
16325
16326 heap_freetuple(newtuple);
16327
16328 return address;
16329}
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 14998 of file tablecmds.c.

15000{
15001 char *colName = cmd->name;
15002 ColumnDef *def = (ColumnDef *) cmd->def;
15003 TypeName *typeName = def->typeName;
15006 attOldTup;
15010 Oid targettype;
15011 int32 targettypmod;
15016 ScanKeyData key[3];
15017 SysScanDesc scan;
15019 ObjectAddress address;
15020
15021 /*
15022 * Clear all the missing values if we're rewriting the table, since this
15023 * renders them pointless.
15024 */
15025 if (tab->rewrite)
15026 {
15028
15032 /* make sure we don't conflict with later attribute modifications */
15034 }
15035
15037
15038 /* Look up the target column */
15040 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
15041 ereport(ERROR,
15043 errmsg("column \"%s\" of relation \"%s\" does not exist",
15046 attnum = attTup->attnum;
15048
15049 /* Check for multiple ALTER TYPE on same column --- can't cope */
15050 if (attTup->atttypid != attOldTup->atttypid ||
15051 attTup->atttypmod != attOldTup->atttypmod)
15052 ereport(ERROR,
15054 errmsg("cannot alter type of column \"%s\" twice",
15055 colName)));
15056
15057 /* Look up the target type (should not fail, since prep found it) */
15058 typeTuple = typenameType(NULL, typeName, &targettypmod);
15060 targettype = tform->oid;
15061 /* And the collation */
15062 targetcollid = GetColumnDefCollation(NULL, def, targettype);
15063
15064 /*
15065 * If there is a default expression for the column, get it and ensure we
15066 * can coerce it to the new datatype. (We must do this before changing
15067 * the column type, because build_column_default itself will try to
15068 * coerce, and will not issue the error message we want if it fails.)
15069 *
15070 * We remove any implicit coercion steps at the top level of the old
15071 * default expression; this has been agreed to satisfy the principle of
15072 * least surprise. (The conversion to the new column type should act like
15073 * it started from what the user sees as the stored expression, and the
15074 * implicit coercions aren't going to be shown.)
15075 */
15076 if (attTup->atthasdef)
15077 {
15081 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
15083 targettype, targettypmod,
15086 -1);
15087 if (defaultexpr == NULL)
15088 {
15089 if (attTup->attgenerated)
15090 ereport(ERROR,
15092 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
15093 colName, format_type_be(targettype))));
15094 else
15095 ereport(ERROR,
15097 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
15098 colName, format_type_be(targettype))));
15099 }
15100 }
15101 else
15102 defaultexpr = NULL;
15103
15104 /*
15105 * Find everything that depends on the column (constraints, indexes, etc),
15106 * and record enough information to let us recreate the objects.
15107 *
15108 * The actual recreation does not happen here, but only after we have
15109 * performed all the individual ALTER TYPE operations. We have to save
15110 * the info before executing ALTER TYPE, though, else the deparser will
15111 * get confused.
15112 */
15114
15115 /*
15116 * Now scan for dependencies of this column on other things. The only
15117 * things we should find are the dependency on the column datatype and
15118 * possibly a collation dependency. Those can be removed.
15119 */
15121
15122 ScanKeyInit(&key[0],
15126 ScanKeyInit(&key[1],
15130 ScanKeyInit(&key[2],
15134
15136 NULL, 3, key);
15137
15139 {
15142
15143 foundObject.classId = foundDep->refclassid;
15144 foundObject.objectId = foundDep->refobjid;
15145 foundObject.objectSubId = foundDep->refobjsubid;
15146
15147 if (foundDep->deptype != DEPENDENCY_NORMAL)
15148 elog(ERROR, "found unexpected dependency type '%c'",
15149 foundDep->deptype);
15150 if (!(foundDep->refclassid == TypeRelationId &&
15151 foundDep->refobjid == attTup->atttypid) &&
15152 !(foundDep->refclassid == CollationRelationId &&
15153 foundDep->refobjid == attTup->attcollation))
15154 elog(ERROR, "found unexpected dependency for column: %s",
15156
15157 CatalogTupleDelete(depRel, &depTup->t_self);
15158 }
15159
15160 systable_endscan(scan);
15161
15163
15164 /*
15165 * Here we go --- change the recorded column type and collation. (Note
15166 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
15167 * fix up the missing value if any.
15168 */
15169 if (attTup->atthasmissing)
15170 {
15172 bool missingNull;
15173
15174 /* if rewrite is true the missing value should already be cleared */
15175 Assert(tab->rewrite == 0);
15176
15177 /* Get the missing value datum */
15180 attrelation->rd_att,
15181 &missingNull);
15182
15183 /* if it's a null array there is nothing to do */
15184
15185 if (!missingNull)
15186 {
15187 /*
15188 * Get the datum out of the array and repack it in a new array
15189 * built with the new type data. We assume that since the table
15190 * doesn't need rewriting, the actual Datum doesn't need to be
15191 * changed, only the array metadata.
15192 */
15193
15194 int one = 1;
15195 bool isNull;
15197 bool nullsAtt[Natts_pg_attribute] = {0};
15198 bool replacesAtt[Natts_pg_attribute] = {0};
15200
15202 1,
15203 &one,
15204 0,
15205 attTup->attlen,
15206 attTup->attbyval,
15207 attTup->attalign,
15208 &isNull);
15210 1,
15211 targettype,
15212 tform->typlen,
15213 tform->typbyval,
15214 tform->typalign));
15215
15219
15223 heapTup = newTup;
15225 }
15226 }
15227
15228 attTup->atttypid = targettype;
15229 attTup->atttypmod = targettypmod;
15230 attTup->attcollation = targetcollid;
15231 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
15232 ereport(ERROR,
15234 errmsg("too many array dimensions"));
15235 attTup->attndims = list_length(typeName->arrayBounds);
15236 attTup->attlen = tform->typlen;
15237 attTup->attbyval = tform->typbyval;
15238 attTup->attalign = tform->typalign;
15239 attTup->attstorage = tform->typstorage;
15240 attTup->attcompression = InvalidCompressionMethod;
15241
15243
15245
15247
15248 /* Install dependencies on new datatype and collation */
15251
15252 /*
15253 * Drop any pg_statistic entry for the column, since it's now wrong type
15254 */
15256
15258 RelationGetRelid(rel), attnum);
15259
15260 /*
15261 * Update the default, if present, by brute force --- remove and re-add
15262 * the default. Probably unsafe to take shortcuts, since the new version
15263 * may well have additional dependencies. (It's okay to do this now,
15264 * rather than after other ALTER TYPE commands, since the default won't
15265 * depend on other column types.)
15266 */
15267 if (defaultexpr)
15268 {
15269 /*
15270 * If it's a GENERATED default, drop its dependency records, in
15271 * particular its INTERNAL dependency on the column, which would
15272 * otherwise cause dependency.c to refuse to perform the deletion.
15273 */
15274 if (attTup->attgenerated)
15275 {
15277
15278 if (!OidIsValid(attrdefoid))
15279 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15280 RelationGetRelid(rel), attnum);
15282 }
15283
15284 /*
15285 * Make updates-so-far visible, particularly the new pg_attribute row
15286 * which will be updated again.
15287 */
15289
15290 /*
15291 * We use RESTRICT here for safety, but at present we do not expect
15292 * anything to depend on the default.
15293 */
15295 true);
15296
15297 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15298 }
15299
15301 RelationGetRelid(rel), attnum);
15302
15303 /* Cleanup */
15305
15306 return address;
15307}
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:1978
void RemoveStatistics(Oid relid, AttrNumber attnum)
Definition heap.c:3509
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:303
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 12301 of file tablecmds.c.

12303{
12306 SysScanDesc scan;
12307 ScanKeyData skey[3];
12310 ObjectAddress address;
12311
12312 /*
12313 * Disallow altering ONLY a partitioned table, as it would make no sense.
12314 * This is okay for legacy inheritance.
12315 */
12316 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12317 ereport(ERROR,
12319 errmsg("constraint must be altered in child tables too"),
12320 errhint("Do not specify the ONLY keyword."));
12321
12322
12325
12326 /*
12327 * Find and check the target constraint
12328 */
12329 ScanKeyInit(&skey[0],
12333 ScanKeyInit(&skey[1],
12337 ScanKeyInit(&skey[2],
12340 CStringGetDatum(cmdcon->conname));
12342 true, NULL, 3, skey);
12343
12344 /* There can be at most one matching row */
12346 ereport(ERROR,
12348 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12349 cmdcon->conname, RelationGetRelationName(rel))));
12350
12352 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12353 ereport(ERROR,
12355 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12356 cmdcon->conname, RelationGetRelationName(rel))));
12357 if (cmdcon->alterEnforceability &&
12358 (currcon->contype != CONSTRAINT_FOREIGN && currcon->contype != CONSTRAINT_CHECK))
12359 ereport(ERROR,
12361 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12362 cmdcon->conname, RelationGetRelationName(rel)),
12363 errhint("Only foreign key and check constraints can change enforceability.")));
12364 if (cmdcon->alterInheritability &&
12365 currcon->contype != CONSTRAINT_NOTNULL)
12366 ereport(ERROR,
12368 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12369 cmdcon->conname, RelationGetRelationName(rel)));
12370
12371 /* Refuse to modify inheritability of inherited constraints */
12372 if (cmdcon->alterInheritability &&
12373 cmdcon->noinherit && currcon->coninhcount > 0)
12374 ereport(ERROR,
12376 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12377 NameStr(currcon->conname),
12379
12380 /*
12381 * If it's not the topmost constraint, raise an error.
12382 *
12383 * Altering a non-topmost constraint leaves some triggers untouched, since
12384 * they are not directly connected to this constraint; also, pg_dump would
12385 * ignore the deferrability status of the individual constraint, since it
12386 * only dumps topmost constraints. Avoid these problems by refusing this
12387 * operation and telling the user to alter the parent constraint instead.
12388 */
12389 if (OidIsValid(currcon->conparentid))
12390 {
12391 HeapTuple tp;
12392 Oid parent = currcon->conparentid;
12393 char *ancestorname = NULL;
12394 char *ancestortable = NULL;
12395
12396 /* Loop to find the topmost constraint */
12398 {
12400
12401 /* If no parent, this is the constraint we want */
12402 if (!OidIsValid(contup->conparentid))
12403 {
12404 ancestorname = pstrdup(NameStr(contup->conname));
12405 ancestortable = get_rel_name(contup->conrelid);
12406 ReleaseSysCache(tp);
12407 break;
12408 }
12409
12410 parent = contup->conparentid;
12411 ReleaseSysCache(tp);
12412 }
12413
12414 ereport(ERROR,
12416 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12417 cmdcon->conname, RelationGetRelationName(rel)),
12419 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12420 cmdcon->conname, ancestorname, ancestortable) : 0,
12421 errhint("You may alter the constraint it derives from instead.")));
12422 }
12423
12424 address = InvalidObjectAddress;
12425
12426 /*
12427 * Do the actual catalog work, and recurse if necessary.
12428 */
12430 contuple, recurse, lockmode))
12432
12433 systable_endscan(scan);
12434
12437
12438 return address;
12439}
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 12446 of file tablecmds.c.

12450{
12452 bool changed = false;
12453 List *otherrelids = NIL;
12454
12456
12457 /*
12458 * Do the catalog work for the enforceability or deferrability change,
12459 * recurse if necessary.
12460 *
12461 * Note that even if deferrability is requested to be altered along with
12462 * enforceability, we don't need to explicitly update multiple entries in
12463 * pg_trigger related to deferrability.
12464 *
12465 * Modifying foreign key enforceability involves either creating or
12466 * dropping the trigger, during which the deferrability setting will be
12467 * adjusted automatically.
12468 */
12469 if (cmdcon->alterEnforceability)
12470 {
12471 if (currcon->contype == CONSTRAINT_FOREIGN)
12473 currcon->conrelid,
12474 currcon->confrelid,
12475 contuple, lockmode,
12478 else if (currcon->contype == CONSTRAINT_CHECK)
12480 contuple, recurse, false,
12481 lockmode);
12482 }
12483 else if (cmdcon->alterDeferrability &&
12485 contuple, recurse, &otherrelids,
12486 lockmode))
12487 {
12488 /*
12489 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12490 * the relations having the constraint itself; here we also invalidate
12491 * for relations that have any triggers that are part of the
12492 * constraint.
12493 */
12494 foreach_oid(relid, otherrelids)
12496
12497 changed = true;
12498 }
12499
12500 /*
12501 * Do the catalog work for the inheritability change.
12502 */
12503 if (cmdcon->alterInheritability &&
12505 lockmode))
12506 changed = true;
12507
12508 return changed;
12509}
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 12830 of file tablecmds.c.

12834{
12836 Oid refrelid;
12837 bool changed = false;
12838
12839 /* since this function recurses, it could be driven to stack overflow */
12841
12842 Assert(cmdcon->alterDeferrability);
12843
12845 refrelid = currcon->confrelid;
12846
12847 /* Should be foreign key constraint */
12848 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12849
12850 /*
12851 * If called to modify a constraint that's already in the desired state,
12852 * silently do nothing.
12853 */
12854 if (currcon->condeferrable != cmdcon->deferrable ||
12855 currcon->condeferred != cmdcon->initdeferred)
12856 {
12858 changed = true;
12859
12860 /*
12861 * Now we need to update the multiple entries in pg_trigger that
12862 * implement the constraint.
12863 */
12865 cmdcon->deferrable,
12866 cmdcon->initdeferred, otherrelids);
12867 }
12868
12869 /*
12870 * If the table at either end of the constraint is partitioned, we need to
12871 * handle every constraint that is a child of this one.
12872 */
12873 if (recurse && changed &&
12874 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12877 contuple, recurse, otherrelids,
12878 lockmode);
12879
12880 return changed;
12881}
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 12887 of file tablecmds.c.

12890{
12893 char *colName;
12894 List *children;
12895
12896 Assert(cmdcon->alterInheritability);
12897
12899
12900 /* The current implementation only works for NOT NULL constraints */
12901 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12902
12903 /*
12904 * If called to modify a constraint that's already in the desired state,
12905 * silently do nothing.
12906 */
12907 if (cmdcon->noinherit == currcon->connoinherit)
12908 return false;
12909
12912
12913 /* Fetch the column number and name */
12915 colName = get_attname(currcon->conrelid, colNum, false);
12916
12917 /*
12918 * Propagate the change to children. For this subcommand type we don't
12919 * recursively affect children, just the immediate level.
12920 */
12922 lockmode);
12923 foreach_oid(childoid, children)
12924 {
12925 ObjectAddress addr;
12926
12927 if (cmdcon->noinherit)
12928 {
12931
12933 if (!childtup)
12934 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12935 colName, childoid);
12937 Assert(childcon->coninhcount > 0);
12938 childcon->coninhcount--;
12939 childcon->conislocal = true;
12942 }
12943 else
12944 {
12946
12947 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12948 colName, true, true, lockmode);
12949 if (OidIsValid(addr.objectId))
12952 }
12953 }
12954
12955 return true;
12956}
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition lsyscache.c:946
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:8028

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

12532{
12534 Oid conoid;
12535 Relation rel;
12536 bool changed = false;
12537
12538 /* Since this function recurses, it could be driven to stack overflow */
12540
12541 Assert(cmdcon->alterEnforceability);
12542
12544 conoid = currcon->oid;
12545
12546 /* Should be foreign key constraint */
12547 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12548
12549 rel = table_open(currcon->conrelid, lockmode);
12550
12551 if (currcon->conenforced != cmdcon->is_enforced)
12552 {
12554 changed = true;
12555 }
12556
12557 /* Drop triggers */
12558 if (!cmdcon->is_enforced)
12559 {
12560 /*
12561 * When setting a constraint to NOT ENFORCED, the constraint triggers
12562 * need to be dropped. Therefore, we must process the child relations
12563 * first, followed by the parent, to account for dependencies.
12564 */
12565 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12569 lockmode, InvalidOid, InvalidOid,
12571
12572 /* Drop all the triggers */
12574 }
12575 else if (changed) /* Create triggers */
12576 {
12581
12582 /* Prepare the minimal information required for trigger creation. */
12584
12585 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12586 fkconstraint->fk_matchtype = currcon->confmatchtype;
12587 fkconstraint->fk_upd_action = currcon->confupdtype;
12588 fkconstraint->fk_del_action = currcon->confdeltype;
12589 fkconstraint->deferrable = currcon->condeferrable;
12590 fkconstraint->initdeferred = currcon->condeferred;
12591
12592 /* Create referenced triggers */
12593 if (currcon->conrelid == fkrelid)
12595 currcon->confrelid,
12597 conoid,
12598 currcon->conindid,
12603
12604 /* Create referencing triggers */
12605 if (currcon->confrelid == pkrelid)
12607 pkrelid,
12609 conoid,
12610 currcon->conindid,
12615
12616 /*
12617 * Tell Phase 3 to check that the constraint is satisfied by existing
12618 * rows. Only applies to leaf partitions, and (for constraints that
12619 * reference a partitioned table) only if this is not one of the
12620 * pg_constraint rows that exist solely to support action triggers.
12621 */
12622 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12623 currcon->confrelid == pkrelid)
12624 {
12625 AlteredTableInfo *tab;
12627
12629 newcon->name = fkconstraint->conname;
12630 newcon->contype = CONSTR_FOREIGN;
12631 newcon->refrelid = currcon->confrelid;
12632 newcon->refindid = currcon->conindid;
12633 newcon->conid = currcon->oid;
12634 newcon->qual = (Node *) fkconstraint;
12635
12636 /* Find or create work queue entry for this table */
12637 tab = ATGetQueueEntry(wqueue, rel);
12638 tab->constraints = lappend(tab->constraints, newcon);
12639 }
12640
12641 /*
12642 * If the table at either end of the constraint is partitioned, we
12643 * need to recurse and create triggers for each constraint that is a
12644 * child of this one.
12645 */
12646 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12650 lockmode,
12655 }
12656
12657 table_close(rel, NoLock);
12658
12659 return changed;
12660}
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 20553 of file tablecmds.c.

20555{
20557 catalog;
20560 SysScanDesc scan;
20562 AttrNumber attno;
20563 int natts;
20565 ObjectAddress address;
20566 const char *trigger_name;
20570 ParseState *pstate = make_parsestate(NULL);
20571
20572 pstate->p_sourcetext = context->queryString;
20573
20574 /*
20575 * We must lock the default partition if one exists, because attaching a
20576 * new partition will change its partition constraint.
20577 */
20582
20584
20585 /*
20586 * XXX I think it'd be a good idea to grab locks on all tables referenced
20587 * by FKs at this point also.
20588 */
20589
20590 /*
20591 * Must be owner of both parent and source table -- parent was checked by
20592 * ATSimplePermissions call in ATPrepCmd
20593 */
20596
20597 /* A partition can only have one parent */
20598 if (attachrel->rd_rel->relispartition)
20599 ereport(ERROR,
20601 errmsg("\"%s\" is already a partition",
20603
20604 if (OidIsValid(attachrel->rd_rel->reloftype))
20605 ereport(ERROR,
20607 errmsg("cannot attach a typed table as partition")));
20608
20609 /*
20610 * Disallow attaching a partition if the table is referenced in a
20611 * publication EXCEPT clause. Changing the partition hierarchy could alter
20612 * the effective publication membership.
20613 */
20615 if (exceptpuboids != NIL)
20616 {
20617 bool first = true;
20619
20621
20623 {
20624 char *pubname = get_publication_name(pubid, false);
20625
20626 if (!first)
20627 {
20628 /*
20629 * translator: This is a separator in a list of publication
20630 * names.
20631 */
20633 }
20634
20635 first = false;
20636
20637 appendStringInfo(&pubnames, _("\"%s\""), pubname);
20638 }
20639
20640 ereport(ERROR,
20642 errmsg_plural("cannot attach table \"%s\" as partition because it is referenced in publication %s EXCEPT clause",
20643 "cannot attach table \"%s\" as partition because it is referenced in publications %s EXCEPT clause",
20646 pubnames.data),
20647 errdetail("The publication EXCEPT clause cannot contain tables that are partitions."),
20648 errhint("Change the publication's EXCEPT clause using ALTER PUBLICATION ... SET ALL TABLES."));
20649 }
20650
20652
20653 /*
20654 * Table being attached should not already be part of inheritance; either
20655 * as a child table...
20656 */
20663 NULL, 1, &skey);
20665 ereport(ERROR,
20667 errmsg("cannot attach inheritance child as partition")));
20668 systable_endscan(scan);
20669
20670 /* ...or as a parent table (except the case when it is partitioned) */
20676 1, &skey);
20678 attachrel->rd_rel->relkind == RELKIND_RELATION)
20679 ereport(ERROR,
20681 errmsg("cannot attach inheritance parent as partition")));
20682 systable_endscan(scan);
20684
20685 /*
20686 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20687 * particular, this disallows making a rel a partition of itself.)
20688 *
20689 * We do that by checking if rel is a member of the list of attachrel's
20690 * partitions provided the latter is partitioned at all. We want to avoid
20691 * having to construct this list again, so we request the strongest lock
20692 * on all partitions. We need the strongest lock, because we may decide
20693 * to scan them if we find out that the table being attached (or its leaf
20694 * partitions) may contain rows that violate the partition constraint. If
20695 * the table has a constraint that would prevent such rows, which by
20696 * definition is present in all the partitions, we need not scan the
20697 * table, nor its partitions. But we cannot risk a deadlock by taking a
20698 * weaker lock now and the stronger one only when needed.
20699 */
20703 ereport(ERROR,
20705 errmsg("circular inheritance not allowed"),
20706 errdetail("\"%s\" is already a child of \"%s\".",
20709
20710 /* If the parent is permanent, so must be all of its partitions. */
20711 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20712 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20713 ereport(ERROR,
20715 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20717
20718 /* Temp parent cannot have a partition that is itself not a temp */
20719 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20720 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20721 ereport(ERROR,
20723 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20725
20726 /* If the parent is temp, it must belong to this session */
20727 if (RELATION_IS_OTHER_TEMP(rel))
20728 ereport(ERROR,
20730 errmsg("cannot attach as partition of temporary relation of another session")));
20731
20732 /* Ditto for the partition */
20734 ereport(ERROR,
20736 errmsg("cannot attach temporary relation of another session as partition")));
20737
20738 /*
20739 * Check if attachrel has any identity columns or any columns that aren't
20740 * in the parent.
20741 */
20743 natts = tupleDesc->natts;
20744 for (attno = 1; attno <= natts; attno++)
20745 {
20747 char *attributeName = NameStr(attribute->attname);
20748
20749 /* Ignore dropped */
20750 if (attribute->attisdropped)
20751 continue;
20752
20753 if (attribute->attidentity)
20754 ereport(ERROR,
20756 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20758 errdetail("The new partition may not contain an identity column."));
20759
20760 /* Try to find the column in parent (matching on column name) */
20764 ereport(ERROR,
20766 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20769 errdetail("The new partition may contain only the columns present in parent.")));
20770 }
20771
20772 /*
20773 * If child_rel has row-level triggers with transition tables, we
20774 * currently don't allow it to become a partition. See also prohibitions
20775 * in ATExecAddInherit() and CreateTrigger().
20776 */
20778 if (trigger_name != NULL)
20779 ereport(ERROR,
20781 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20783 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20784
20785 /*
20786 * Check that the new partition's bound is valid and does not overlap any
20787 * of existing partitions of the parent - note that it does not return on
20788 * error.
20789 */
20791 cmd->bound, pstate);
20792
20794
20795 /*
20796 * Generate a partition constraint from the partition bound specification.
20797 * If the parent itself is a partition, make sure to include its
20798 * constraint as well.
20799 */
20801
20802 /*
20803 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20804 * since it's needed later to construct the constraint expression for
20805 * validating against the default partition, if any.
20806 */
20809
20810 /* Skip validation if there are no constraints to validate. */
20811 if (partConstraint)
20812 {
20813 /*
20814 * Run the partition quals through const-simplification similar to
20815 * check constraints. We skip canonicalize_qual, though, because
20816 * partition quals should be in canonical form already.
20817 */
20820 (Node *) partConstraint);
20821
20822 /* XXX this sure looks wrong */
20824
20825 /*
20826 * Adjust the generated constraint to match this partition's attribute
20827 * numbers.
20828 */
20830 rel);
20831
20832 /* Validate partition constraints against the table being attached. */
20834 false);
20835 }
20836
20837 /*
20838 * If we're attaching a partition other than the default partition and a
20839 * default one exists, then that partition's partition constraint changes,
20840 * so add an entry to the work queue to validate it, too. (We must not do
20841 * this when the partition being attached is the default one; we already
20842 * did it above!)
20843 */
20845 {
20848
20849 Assert(!cmd->bound->is_default);
20850
20851 /* we already hold a lock on the default partition */
20855
20856 /*
20857 * Map the Vars in the constraint expression from rel's attnos to
20858 * defaultrel's.
20859 */
20862 1, defaultrel, rel);
20864 defPartConstraint, true);
20865
20866 /* keep our lock until commit. */
20868 }
20869
20871
20872 /*
20873 * If the partition we just attached is partitioned itself, invalidate
20874 * relcache for all descendent partitions too to ensure that their
20875 * rd_partcheck expression trees are rebuilt; partitions already locked at
20876 * the beginning of this function.
20877 */
20878 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20879 {
20880 ListCell *l;
20881
20882 foreach(l, attachrel_children)
20883 {
20885 }
20886 }
20887
20888 /* keep our lock until commit */
20890
20891 return address;
20892}
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:3865
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 21911 of file tablecmds.c.

21912{
21915 Relation parentTbl;
21916 ObjectAddress address;
21917 Oid partIdxId;
21920
21921 /*
21922 * We need to obtain lock on the index 'name' to modify it, but we also
21923 * need to read its owning table's tuple descriptor -- so we need to lock
21924 * both. To avoid deadlocks, obtain lock on the table before doing so on
21925 * the index. Furthermore, we need to examine the parent table of the
21926 * partition, so lock that one too.
21927 */
21928 state.partitionOid = InvalidOid;
21929 state.parentTblOid = parentIdx->rd_index->indrelid;
21930 state.lockedParentTbl = false;
21931 partIdxId =
21934 &state);
21935 /* Not there? */
21936 if (!OidIsValid(partIdxId))
21937 ereport(ERROR,
21939 errmsg("index \"%s\" does not exist", name->relname)));
21940
21941 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21943
21944 /* we already hold locks on both tables, so this is safe: */
21945 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21946 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21947
21949
21950 /*
21951 * Check if the index is already attached to the correct parent,
21952 * ultimately attempting one round of validation if already the case.
21953 */
21954 currParent = partIdx->rd_rel->relispartition ?
21956 if (currParent != RelationGetRelid(parentIdx))
21957 {
21960 AttrMap *attmap;
21961 bool found;
21962 int i;
21966
21967 /*
21968 * If this partition already has an index attached, refuse the
21969 * operation.
21970 */
21972
21974 ereport(ERROR,
21976 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21978 RelationGetRelationName(parentIdx)),
21979 errdetail("Index \"%s\" is already attached to another index.",
21981
21982 /* Make sure it indexes a partition of the other index's table */
21983 partDesc = RelationGetPartitionDesc(parentTbl, true);
21984 found = false;
21985 for (i = 0; i < partDesc->nparts; i++)
21986 {
21987 if (partDesc->oids[i] == state.partitionOid)
21988 {
21989 found = true;
21990 break;
21991 }
21992 }
21993 if (!found)
21994 ereport(ERROR,
21996 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21998 RelationGetRelationName(parentIdx)),
21999 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
22001 RelationGetRelationName(parentTbl))));
22002
22003 /* Ensure the indexes are compatible */
22005 parentInfo = BuildIndexInfo(parentIdx);
22007 RelationGetDescr(parentTbl),
22008 false);
22010 partIdx->rd_indcollation,
22011 parentIdx->rd_indcollation,
22012 partIdx->rd_opfamily,
22013 parentIdx->rd_opfamily,
22014 attmap))
22015 ereport(ERROR,
22017 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22019 RelationGetRelationName(parentIdx)),
22020 errdetail("The index definitions do not match.")));
22021
22022 /*
22023 * If there is a constraint in the parent, make sure there is one in
22024 * the child too.
22025 */
22027 RelationGetRelid(parentIdx));
22028
22030 {
22032 partIdxId);
22033 if (!OidIsValid(cldConstrId))
22034 ereport(ERROR,
22036 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22038 RelationGetRelationName(parentIdx)),
22039 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
22040 RelationGetRelationName(parentIdx),
22041 RelationGetRelationName(parentTbl),
22043 }
22044
22045 /*
22046 * If it's a primary key, make sure the columns in the partition are
22047 * NOT NULL.
22048 */
22049 if (parentIdx->rd_index->indisprimary)
22051
22052 /* All good -- do it */
22057
22059
22060 validatePartitionedIndex(parentIdx, parentTbl);
22061 }
22062 else if (!parentIdx->rd_index->indisvalid)
22063 {
22064 /*
22065 * The index is attached, but the parent is still invalid; see if it
22066 * can be validated now.
22067 */
22068 validatePartitionedIndex(parentIdx, parentTbl);
22069 }
22070
22071 relation_close(parentTbl, AccessShareLock);
22072 /* keep these locks till commit */
22075
22076 return address;
22077}
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 16344 of file tablecmds.c.

16345{
16348 HeapTuple tuple;
16350
16351 /*
16352 * Get exclusive lock till end of transaction on the target table. Use
16353 * relation_open so that we can work on indexes and sequences.
16354 */
16355 target_rel = relation_open(relationOid, lockmode);
16356
16357 /* Get its pg_class tuple, too */
16359
16360 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16361 if (!HeapTupleIsValid(tuple))
16362 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16364
16365 /* Can we change the ownership of this tuple? */
16366 switch (tuple_class->relkind)
16367 {
16368 case RELKIND_RELATION:
16369 case RELKIND_VIEW:
16370 case RELKIND_MATVIEW:
16373 case RELKIND_PROPGRAPH:
16374 /* ok to change owner */
16375 break;
16376 case RELKIND_INDEX:
16377 if (!recursing)
16378 {
16379 /*
16380 * Because ALTER INDEX OWNER used to be allowed, and in fact
16381 * is generated by old versions of pg_dump, we give a warning
16382 * and do nothing rather than erroring out. Also, to avoid
16383 * unnecessary chatter while restoring those old dumps, say
16384 * nothing at all if the command would be a no-op anyway.
16385 */
16386 if (tuple_class->relowner != newOwnerId)
16389 errmsg("cannot change owner of index \"%s\"",
16390 NameStr(tuple_class->relname)),
16391 errhint("Change the ownership of the index's table instead.")));
16392 /* quick hack to exit via the no-op path */
16393 newOwnerId = tuple_class->relowner;
16394 }
16395 break;
16397 if (recursing)
16398 break;
16399 ereport(ERROR,
16401 errmsg("cannot change owner of index \"%s\"",
16402 NameStr(tuple_class->relname)),
16403 errhint("Change the ownership of the index's table instead.")));
16404 break;
16405 case RELKIND_SEQUENCE:
16406 if (!recursing &&
16407 tuple_class->relowner != newOwnerId)
16408 {
16409 /* if it's an owned sequence, disallow changing it by itself */
16410 Oid tableId;
16411 int32 colId;
16412
16413 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16415 ereport(ERROR,
16417 errmsg("cannot change owner of sequence \"%s\"",
16418 NameStr(tuple_class->relname)),
16419 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16420 NameStr(tuple_class->relname),
16422 }
16423 break;
16425 if (recursing)
16426 break;
16427 ereport(ERROR,
16429 errmsg("\"%s\" is a composite type",
16430 NameStr(tuple_class->relname)),
16431 /* translator: %s is an SQL ALTER command */
16432 errhint("Use %s instead.",
16433 "ALTER TYPE")));
16434 break;
16435 case RELKIND_TOASTVALUE:
16436 if (recursing)
16437 break;
16439 default:
16440 ereport(ERROR,
16442 errmsg("cannot change owner of relation \"%s\"",
16443 NameStr(tuple_class->relname)),
16445 }
16446
16447 /*
16448 * If the new owner is the same as the existing owner, consider the
16449 * command to have succeeded. This is for dump restoration purposes.
16450 */
16451 if (tuple_class->relowner != newOwnerId)
16452 {
16456 Acl *newAcl;
16458 bool isNull;
16459 HeapTuple newtuple;
16460
16461 /* skip permission checks when recursing to index or toast table */
16462 if (!recursing)
16463 {
16464 /* Superusers can always do it */
16465 if (!superuser())
16466 {
16467 Oid namespaceOid = tuple_class->relnamespace;
16469
16470 /* Otherwise, must be owner of the existing object */
16471 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16474
16475 /* Must be able to become new owner */
16477
16478 /* New owner must have CREATE privilege on namespace */
16480 ACL_CREATE);
16481 if (aclresult != ACLCHECK_OK)
16484 }
16485 }
16486
16487 memset(repl_null, false, sizeof(repl_null));
16488 memset(repl_repl, false, sizeof(repl_repl));
16489
16492
16493 /*
16494 * Determine the modified ACL for the new owner. This is only
16495 * necessary when the ACL is non-null.
16496 */
16499 &isNull);
16500 if (!isNull)
16501 {
16503 tuple_class->relowner, newOwnerId);
16504 repl_repl[Anum_pg_class_relacl - 1] = true;
16506 }
16507
16509
16510 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16511
16512 heap_freetuple(newtuple);
16513
16514 /*
16515 * We must similarly update any per-column ACLs to reflect the new
16516 * owner; for neatness reasons that's split out as a subroutine.
16517 */
16518 change_owner_fix_column_acls(relationOid,
16519 tuple_class->relowner,
16520 newOwnerId);
16521
16522 /*
16523 * Update owner dependency reference, if any. A composite type has
16524 * none, because it's tracked for the pg_type entry instead of here;
16525 * indexes and TOAST tables don't have their own entries either.
16526 */
16527 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16528 tuple_class->relkind != RELKIND_INDEX &&
16530 tuple_class->relkind != RELKIND_TOASTVALUE)
16532 newOwnerId);
16533
16534 /*
16535 * Also change the ownership of the table's row type, if it has one
16536 */
16537 if (OidIsValid(tuple_class->reltype))
16539
16540 /*
16541 * If we are operating on a table or materialized view, also change
16542 * the ownership of any indexes and sequences that belong to the
16543 * relation, as well as its toast table (if it has one).
16544 */
16545 if (tuple_class->relkind == RELKIND_RELATION ||
16547 tuple_class->relkind == RELKIND_MATVIEW ||
16548 tuple_class->relkind == RELKIND_TOASTVALUE)
16549 {
16551 ListCell *i;
16552
16553 /* Find all the indexes belonging to this relation */
16555
16556 /* For each index, recursively change its ownership */
16557 foreach(i, index_oid_list)
16558 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16559
16561 }
16562
16563 /* If it has a toast table, recurse to change its ownership */
16564 if (tuple_class->reltoastrelid != InvalidOid)
16566 true, lockmode);
16567
16568 /* If it has dependent sequences, recurse to change them too */
16569 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16570 }
16571
16573
16574 ReleaseSysCache(tuple);
16577}
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:4030

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

16721{
16722 Oid indexOid;
16723 ObjectAddress address;
16724
16725 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16726
16727 if (!OidIsValid(indexOid))
16728 ereport(ERROR,
16730 errmsg("index \"%s\" for table \"%s\" does not exist",
16732
16733 /* Check index is valid to cluster on */
16734 check_index_is_clusterable(rel, indexOid, lockmode);
16735
16736 /* And do the work */
16737 mark_index_clustered(rel, indexOid, false);
16738
16739 ObjectAddressSet(address,
16740 RelationRelationId, indexOid);
16741
16742 return address;
16743}
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition repack.c:752
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition repack.c:812

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:8904
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition tablecmds.c:8241
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition tablecmds.c:8603
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:7857
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition tablecmds.c:9899
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:9153
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:9386
static void ATExecSetRowSecurity(Relation rel, bool rls)
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition tablecmds.c:8326
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:8486
static void ATExecGenericOptions(Relation rel, List *options)
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition tablecmds.c:9008
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:8717
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:9807
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:9295
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:9722
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:9786
static ObjectAddress ATExecClusterOn(Relation rel, const char *indexName, LOCKMODE lockmode)
static void ATExecEnableDisableRule(Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
#define TRIGGER_DISABLED
Definition trigger.h:154
#define TRIGGER_FIRES_ON_REPLICA
Definition trigger.h:153
#define TRIGGER_FIRES_ALWAYS
Definition trigger.h:152
ObjectAddress AlterDomainAddConstraint(List *names, Node *newConstraint, ObjectAddress *constrAddr)
Definition typecmds.c:2960

References AlterDomainAddConstraint(), Assert, AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddIdentity, AT_AddIndex, AT_AddIndexConstraint, AT_AddInherit, AT_AddOf, AT_AlterColumnGenericOptions, AT_AlterColumnType, AT_AlterConstraint, AT_AttachPartition, AT_ChangeOwner, AT_ClusterOn, AT_ColumnDefault, AT_CookedColumnDefault, AT_DetachPartition, AT_DetachPartitionFinalize, AT_DisableRowSecurity, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropExpression, AT_DropIdentity, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRowSecurity, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_ForceRowSecurity, AT_GenericOptions, AT_MergePartitions, AT_NoForceRowSecurity, AT_PASS_ADD_CONSTR, AT_ReAddComment, AT_ReAddConstraint, AT_ReAddDomainConstraint, AT_ReAddIndex, AT_ReAddStatistics, AT_ReplaceRelOptions, AT_ReplicaIdentity, AT_ResetOptions, AT_ResetRelOptions, AT_SetAccessMethod, AT_SetCompression, AT_SetExpression, AT_SetIdentity, AT_SetLogged, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_SetUnLogged, AT_SplitPartition, AT_ValidateConstraint, ATExecAddColumn(), ATExecAddConstraint(), ATExecAddIdentity(), ATExecAddIndex(), ATExecAddIndexConstraint(), ATExecAddInherit(), ATExecAddOf(), ATExecAddStatistics(), ATExecAlterColumnGenericOptions(), ATExecAlterColumnType(), ATExecAlterConstraint(), ATExecAttachPartition(), ATExecAttachPartitionIdx(), ATExecChangeOwner(), ATExecClusterOn(), ATExecColumnDefault(), ATExecCookedColumnDefault(), ATExecDetachPartition(), ATExecDetachPartitionFinalize(), ATExecDropCluster(), ATExecDropColumn(), ATExecDropConstraint(), ATExecDropExpression(), ATExecDropIdentity(), ATExecDropInherit(), ATExecDropNotNull(), ATExecDropOf(), ATExecEnableDisableRule(), ATExecEnableDisableTrigger(), ATExecForceNoForceRowSecurity(), ATExecGenericOptions(), ATExecMergePartitions(), ATExecReplicaIdentity(), ATExecSetAccessMethodNoStorage(), ATExecSetCompression(), ATExecSetExpression(), ATExecSetIdentity(), ATExecSetNotNull(), ATExecSetOptions(), ATExecSetRelOptions(), ATExecSetRowSecurity(), ATExecSetStatistics(), ATExecSetStorage(), ATExecSetTableSpaceNoStorage(), ATExecSplitPartition(), ATExecValidateConstraint(), ATParseTransformCmd(), AlterTableCmd::behavior, castNode, AlteredTableInfo::chgAccessMethod, CommandCounterIncrement(), CommentObject(), AlterTableCmd::def, elog, ERROR, EventTriggerCollectAlterTableSubcmd(), fb(), get_rolespec_oid(), InvalidObjectAddress, AlterTableCmd::missing_ok, AlterTableCmd::name, AlteredTableInfo::newAccessMethod, AlterTableCmd::newowner, AlteredTableInfo::newTableSpace, AlterTableCmd::num, RelationData::rd_rel, AlterTableCmd::recurse, AlteredTableInfo::rel, RelationGetRelid, RULE_DISABLED, RULE_FIRES_ALWAYS, RULE_FIRES_ON_ORIGIN, RULE_FIRES_ON_REPLICA, AlterTableCmd::subtype, TRIGGER_DISABLED, TRIGGER_FIRES_ALWAYS, TRIGGER_FIRES_ON_ORIGIN, and TRIGGER_FIRES_ON_REPLICA.

Referenced by ATRewriteCatalogs().

◆ ATExecColumnDefault()

static ObjectAddress ATExecColumnDefault ( Relation  rel,
const char colName,
Node newDefault,
LOCKMODE  lockmode 
)
static

Definition at line 8241 of file tablecmds.c.

8243{
8244 TupleDesc tupdesc = RelationGetDescr(rel);
8246 ObjectAddress address;
8247
8248 /*
8249 * get the number of the attribute
8250 */
8253 ereport(ERROR,
8255 errmsg("column \"%s\" of relation \"%s\" does not exist",
8257
8258 /* Prevent them from altering a system attribute */
8259 if (attnum <= 0)
8260 ereport(ERROR,
8262 errmsg("cannot alter system column \"%s\"",
8263 colName)));
8264
8265 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8266 ereport(ERROR,
8268 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8270 /* translator: %s is an SQL ALTER command */
8271 newDefault ? 0 : errhint("Use %s instead.",
8272 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8273
8274 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8275 ereport(ERROR,
8277 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8279 newDefault ?
8280 /* translator: %s is an SQL ALTER command */
8281 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8282 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8283 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8284
8285 /*
8286 * Remove any old default for the column. We use RESTRICT here for
8287 * safety, but at present we do not expect anything to depend on the
8288 * default.
8289 *
8290 * We treat removing the existing default as an internal operation when it
8291 * is preparatory to adding a new default, but as a user-initiated
8292 * operation when the user asked for a drop.
8293 */
8295 newDefault != NULL);
8296
8297 if (newDefault)
8298 {
8299 /* SET DEFAULT */
8301
8303 rawEnt->attnum = attnum;
8304 rawEnt->raw_default = newDefault;
8305 rawEnt->generated = '\0';
8306
8307 /*
8308 * This function is intended for CREATE TABLE, so it processes a
8309 * _list_ of defaults, but we just do one.
8310 */
8312 false, true, false, NULL);
8313 }
8314
8316 RelationGetRelid(rel), attnum);
8317 return address;
8318}
#define InvalidAttrNumber
Definition attnum.h:23
AttrNumber get_attnum(Oid relid, const char *attname)
Definition lsyscache.c:977

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

8328{
8329 ObjectAddress address;
8330
8331 /* We assume no checking is required */
8332
8333 /*
8334 * Remove any old default for the column. We use RESTRICT here for
8335 * safety, but at present we do not expect anything to depend on the
8336 * default. (In ordinary cases, there could not be a default in place
8337 * anyway, but it's possible when combining LIKE with inheritance.)
8338 */
8340 true);
8341
8342 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8343
8345 RelationGetRelid(rel), attnum);
8346 return address;
8347}

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

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

21751{
21752 Relation partRel;
21753 ObjectAddress address;
21755
21757
21758 /*
21759 * Wait until existing snapshots are gone. This is important if the
21760 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21761 * user could immediately run DETACH FINALIZE without actually waiting for
21762 * existing transactions. We must not complete the detach action until
21763 * all such queries are complete (otherwise we would present them with an
21764 * inconsistent view of catalogs).
21765 */
21766 WaitForOlderSnapshots(snap->xmin, false);
21767
21768 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21769
21771
21772 table_close(partRel, NoLock);
21773
21774 return address;
21775}
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 16752 of file tablecmds.c.

16753{
16754 mark_index_clustered(rel, InvalidOid, false);
16755}

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

9391{
9392 HeapTuple tuple;
9395 List *children;
9396 ObjectAddress object;
9397 bool is_expr;
9398
9399 /* At top level, permission check was done in ATPrepCmd, else do it */
9400 if (recursing)
9403
9404 /* Initialize addrs on the first invocation */
9405 Assert(!recursing || addrs != NULL);
9406
9407 /* since this function recurses, it could be driven to stack overflow */
9409
9410 if (!recursing)
9411 addrs = new_object_addresses();
9412
9413 /*
9414 * get the number of the attribute
9415 */
9417 if (!HeapTupleIsValid(tuple))
9418 {
9419 if (!missing_ok)
9420 {
9421 ereport(ERROR,
9423 errmsg("column \"%s\" of relation \"%s\" does not exist",
9425 }
9426 else
9427 {
9429 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9431 return InvalidObjectAddress;
9432 }
9433 }
9435
9436 attnum = targetatt->attnum;
9437
9438 /* Can't drop a system attribute */
9439 if (attnum <= 0)
9440 ereport(ERROR,
9442 errmsg("cannot drop system column \"%s\"",
9443 colName)));
9444
9445 /*
9446 * Don't drop inherited columns, unless recursing (presumably from a drop
9447 * of the parent column)
9448 */
9449 if (targetatt->attinhcount > 0 && !recursing)
9450 ereport(ERROR,
9452 errmsg("cannot drop inherited column \"%s\"",
9453 colName)));
9454
9455 /*
9456 * Don't drop columns used in the partition key, either. (If we let this
9457 * go through, the key column's dependencies would cause a cascaded drop
9458 * of the whole table, which is surely not what the user expected.)
9459 */
9460 if (has_partition_attrs(rel,
9462 &is_expr))
9463 ereport(ERROR,
9465 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9467
9468 ReleaseSysCache(tuple);
9469
9470 /*
9471 * Propagate to children as appropriate. Unlike most other ALTER
9472 * routines, we have to do this one level of recursion at a time; we can't
9473 * use find_all_inheritors to do it in one pass.
9474 */
9475 children =
9477
9478 if (children)
9479 {
9481 ListCell *child;
9482
9483 /*
9484 * In case of a partitioned table, the column must be dropped from the
9485 * partitions as well.
9486 */
9487 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9488 ereport(ERROR,
9490 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9491 errhint("Do not specify the ONLY keyword.")));
9492
9494 foreach(child, children)
9495 {
9496 Oid childrelid = lfirst_oid(child);
9499
9500 /* find_inheritance_children already got lock */
9503
9505 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9506 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9509
9510 if (childatt->attinhcount <= 0) /* shouldn't happen */
9511 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9513
9514 if (recurse)
9515 {
9516 /*
9517 * If the child column has other definition sources, just
9518 * decrement its inheritance count; if not, recurse to delete
9519 * it.
9520 */
9521 if (childatt->attinhcount == 1 && !childatt->attislocal)
9522 {
9523 /* Time to delete this child column, too */
9525 behavior, true, true,
9526 false, lockmode, addrs);
9527 }
9528 else
9529 {
9530 /* Child column must survive my deletion */
9531 childatt->attinhcount--;
9532
9533 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9534
9535 /* Make update visible */
9537 }
9538 }
9539 else
9540 {
9541 /*
9542 * If we were told to drop ONLY in this table (no recursion),
9543 * we need to mark the inheritors' attributes as locally
9544 * defined rather than inherited.
9545 */
9546 childatt->attinhcount--;
9547 childatt->attislocal = true;
9548
9549 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9550
9551 /* Make update visible */
9553 }
9554
9555 heap_freetuple(tuple);
9556
9558 }
9560 }
9561
9562 /* Add object to delete */
9563 object.classId = RelationRelationId;
9564 object.objectId = RelationGetRelid(rel);
9565 object.objectSubId = attnum;
9566 add_exact_object_address(&object, addrs);
9567
9568 if (!recursing)
9569 {
9570 /* Recursion has ended, drop everything that was collected */
9571 performMultipleDeletions(addrs, behavior, 0);
9572 free_object_addresses(addrs);
9573 }
9574
9575 return object;
9576}
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 14284 of file tablecmds.c.

14287{
14289 SysScanDesc scan;
14290 ScanKeyData skey[3];
14291 HeapTuple tuple;
14292 bool found = false;
14293
14295
14296 /*
14297 * Find and drop the target constraint
14298 */
14299 ScanKeyInit(&skey[0],
14303 ScanKeyInit(&skey[1],
14307 ScanKeyInit(&skey[2],
14312 true, NULL, 3, skey);
14313
14314 /* There can be at most one matching row */
14315 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14316 {
14317 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14318 missing_ok, lockmode);
14319 found = true;
14320 }
14321
14322 systable_endscan(scan);
14323
14324 if (!found)
14325 {
14326 if (!missing_ok)
14327 ereport(ERROR,
14329 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14331 else
14333 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14335 }
14336
14338}
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 8904 of file tablecmds.c.

8905{
8906 HeapTuple tuple;
8911 ObjectAddress address;
8912
8915 if (!HeapTupleIsValid(tuple))
8916 ereport(ERROR,
8918 errmsg("column \"%s\" of relation \"%s\" does not exist",
8920
8922 attnum = attTup->attnum;
8923
8924 if (attnum <= 0)
8925 ereport(ERROR,
8927 errmsg("cannot alter system column \"%s\"",
8928 colName)));
8929
8930 /*
8931 * TODO: This could be done, but it would need a table rewrite to
8932 * materialize the generated values. Note that for the time being, we
8933 * still error with missing_ok, so that we don't silently leave the column
8934 * as generated.
8935 */
8936 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8937 ereport(ERROR,
8939 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8940 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8942
8943 if (!attTup->attgenerated)
8944 {
8945 if (!missing_ok)
8946 ereport(ERROR,
8948 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8950 else
8951 {
8953 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8955 heap_freetuple(tuple);
8957 return InvalidObjectAddress;
8958 }
8959 }
8960
8961 /*
8962 * Mark the column as no longer generated. (The atthasdef flag needs to
8963 * get cleared too, but RemoveAttrDefault will handle that.)
8964 */
8965 attTup->attgenerated = '\0';
8966 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8967
8969 RelationGetRelid(rel),
8970 attnum);
8971 heap_freetuple(tuple);
8972
8974
8975 /*
8976 * Drop the dependency records of the GENERATED expression, in particular
8977 * its INTERNAL dependency on the column, which would otherwise cause
8978 * dependency.c to refuse to perform the deletion.
8979 */
8981 if (!OidIsValid(attrdefoid))
8982 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8983 RelationGetRelid(rel), attnum);
8985
8986 /* Make above changes visible */
8988
8989 /*
8990 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8991 * safety, but at present we do not expect anything to depend on the
8992 * default.
8993 */
8995 false, false);
8996
8998 RelationGetRelid(rel), attnum);
8999 return address;
9000}

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

8605{
8606 HeapTuple tuple;
8610 ObjectAddress address;
8611 Oid seqid;
8613 bool ispartitioned;
8614
8615 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8616 if (ispartitioned && !recurse)
8617 ereport(ERROR,
8619 errmsg("cannot drop identity from a column of only the partitioned table"),
8620 errhint("Do not specify the ONLY keyword.")));
8621
8622 if (rel->rd_rel->relispartition && !recursing)
8623 ereport(ERROR,
8625 errmsg("cannot drop identity from a column of a partition"));
8626
8629 if (!HeapTupleIsValid(tuple))
8630 ereport(ERROR,
8632 errmsg("column \"%s\" of relation \"%s\" does not exist",
8634
8636 attnum = attTup->attnum;
8637
8638 if (attnum <= 0)
8639 ereport(ERROR,
8641 errmsg("cannot alter system column \"%s\"",
8642 colName)));
8643
8644 if (!attTup->attidentity)
8645 {
8646 if (!missing_ok)
8647 ereport(ERROR,
8649 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8651 else
8652 {
8654 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8656 heap_freetuple(tuple);
8658 return InvalidObjectAddress;
8659 }
8660 }
8661
8662 attTup->attidentity = '\0';
8663 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8664
8666 RelationGetRelid(rel),
8667 attTup->attnum);
8669 RelationGetRelid(rel), attnum);
8670 heap_freetuple(tuple);
8671
8673
8674 /*
8675 * Recurse to drop the identity from column in partitions. Identity is
8676 * not inherited in regular inheritance children so ignore them.
8677 */
8678 if (recurse && ispartitioned)
8679 {
8680 List *children;
8681 ListCell *lc;
8682
8683 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8684
8685 foreach(lc, children)
8686 {
8688
8690 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8692 }
8693 }
8694
8695 if (!recursing)
8696 {
8697 /* drop the internal sequence */
8698 seqid = getIdentitySequence(rel, attnum, false);
8703 seqaddress.objectId = seqid;
8704 seqaddress.objectSubId = 0;
8706 }
8707
8708 return address;
8709}
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:353
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition pg_depend.c:1018

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

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

◆ ATExecDropInherit()

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

Definition at line 18094 of file tablecmds.c.

18095{
18096 ObjectAddress address;
18098
18099 /*
18100 * AccessShareLock on the parent is probably enough, seeing that DROP
18101 * TABLE doesn't lock parent tables at all. We need some lock since we'll
18102 * be inspecting the parent's schema.
18103 */
18105
18106 /*
18107 * We don't bother to check ownership of the parent table --- ownership of
18108 * the child is presumed enough rights.
18109 */
18110
18111 /* Off to RemoveInheritance() where most of the work happens */
18112 RemoveInheritance(rel, parent_rel, false);
18113
18116
18117 /* keep our lock on the parent relation until commit */
18119
18120 return address;
18121}

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

7859{
7860 HeapTuple tuple;
7865 ObjectAddress address;
7866
7867 /*
7868 * lookup the attribute
7869 */
7871
7873 if (!HeapTupleIsValid(tuple))
7874 ereport(ERROR,
7876 errmsg("column \"%s\" of relation \"%s\" does not exist",
7879 attnum = attTup->attnum;
7881 RelationGetRelid(rel), attnum);
7882
7883 /* If the column is already nullable there's nothing to do. */
7884 if (!attTup->attnotnull)
7885 {
7887 return InvalidObjectAddress;
7888 }
7889
7890 /* Prevent them from altering a system attribute */
7891 if (attnum <= 0)
7892 ereport(ERROR,
7894 errmsg("cannot alter system column \"%s\"",
7895 colName)));
7896
7897 if (attTup->attidentity)
7898 ereport(ERROR,
7900 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7902
7903 /*
7904 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7905 */
7906 if (rel->rd_rel->relispartition)
7907 {
7910 TupleDesc tupDesc = RelationGetDescr(parent);
7912
7914 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7915 ereport(ERROR,
7917 errmsg("column \"%s\" is marked NOT NULL in parent table",
7918 colName)));
7920 }
7921
7922 /*
7923 * Find the constraint that makes this column NOT NULL, and drop it.
7924 * dropconstraint_internal() resets attnotnull.
7925 */
7927 if (conTup == NULL)
7928 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7930
7931 /* The normal case: we have a pg_constraint row, remove it */
7932 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7933 false, lockmode);
7935
7937 RelationGetRelid(rel), attnum);
7938
7940
7941 return address;
7942}
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 18622 of file tablecmds.c.

18623{
18624 Oid relid = RelationGetRelid(rel);
18626 HeapTuple tuple;
18627
18628 if (!OidIsValid(rel->rd_rel->reloftype))
18629 ereport(ERROR,
18631 errmsg("\"%s\" is not a typed table",
18633
18634 /*
18635 * We don't bother to check ownership of the type --- ownership of the
18636 * table is presumed enough rights. No lock required on the type, either.
18637 */
18638
18639 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18641
18642 /* Clear pg_class.reloftype */
18645 if (!HeapTupleIsValid(tuple))
18646 elog(ERROR, "cache lookup failed for relation %u", relid);
18647 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18649
18651
18652 heap_freetuple(tuple);
18654}

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

17497{
17498 EnableDisableRule(rel, rulename, fires_when);
17499
17501 RelationGetRelid(rel), 0);
17502}
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 17477 of file tablecmds.c.

17480{
17481 EnableDisableTrigger(rel, trigname, InvalidOid,
17482 fires_when, skip_system, recurse,
17483 lockmode);
17484
17486 RelationGetRelid(rel), 0);
17487}
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 18898 of file tablecmds.c.

18899{
18901 Oid relid;
18902 HeapTuple tuple;
18903
18904 relid = RelationGetRelid(rel);
18905
18907
18909
18910 if (!HeapTupleIsValid(tuple))
18911 elog(ERROR, "cache lookup failed for relation %u", relid);
18912
18914 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18915
18917 RelationGetRelid(rel), 0);
18918
18920 heap_freetuple(tuple);
18921}

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

18928{
18930 ForeignServer *server;
18932 HeapTuple tuple;
18933 bool isnull;
18937 Datum datum;
18939
18940 if (options == NIL)
18941 return;
18942
18944
18946 ObjectIdGetDatum(rel->rd_id));
18947 if (!HeapTupleIsValid(tuple))
18948 ereport(ERROR,
18950 errmsg("foreign table \"%s\" does not exist",
18953 server = GetForeignServer(tableform->ftserver);
18954 fdw = GetForeignDataWrapper(server->fdwid);
18955
18956 memset(repl_val, 0, sizeof(repl_val));
18957 memset(repl_null, false, sizeof(repl_null));
18958 memset(repl_repl, false, sizeof(repl_repl));
18959
18960 /* Extract the current options */
18962 tuple,
18964 &isnull);
18965 if (isnull)
18966 datum = PointerGetDatum(NULL);
18967
18968 /* Transform the options */
18970 datum,
18971 options,
18972 fdw->fdwvalidator);
18973
18974 if (DatumGetPointer(datum) != NULL)
18976 else
18978
18980
18981 /* Everything looks good - update the tuple */
18982
18985
18986 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18987
18988 /*
18989 * Invalidate relcache so that all sessions will refresh any cached plans
18990 * that might depend on the old options.
18991 */
18993
18995 RelationGetRelid(rel), 0);
18996
18998
18999 heap_freetuple(tuple);
19000}

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

23264{
23267 List *extDepState = NIL;
23270 Oid ownerId = InvalidOid;
23271 Oid save_userid;
23272 int save_sec_context;
23273 int save_nestlevel;
23274
23275 /*
23276 * Check ownership of merged partitions - partitions with different owners
23277 * cannot be merged. Also, collect the OIDs of these partitions during the
23278 * check.
23279 */
23281 {
23283
23284 /*
23285 * We are going to detach and remove this partition. We already took
23286 * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
23287 * NoLock is fine.
23288 */
23291
23292 if (OidIsValid(ownerId))
23293 {
23294 /* Do the partitions being merged have different owners? */
23295 if (ownerId != mergingPartition->rd_rel->relowner)
23296 ereport(ERROR,
23298 errmsg("partitions being merged have different owners"));
23299 }
23300 else
23301 ownerId = mergingPartition->rd_rel->relowner;
23302
23303 /* Store the next merging partition into the list. */
23306
23308 }
23309
23310 /* Look up the existing relation by the new partition name. */
23312
23313 /*
23314 * Check if this name is already taken. This helps us to detect the
23315 * situation when one of the merging partitions has the same name as the
23316 * new partition. Otherwise, this would fail later on anyway, but
23317 * catching this here allows us to emit a nicer error message.
23318 */
23320 {
23322 {
23323 /*
23324 * The new partition has the same name as one of the merging
23325 * partitions.
23326 */
23327 char tmpRelName[NAMEDATALEN];
23328
23329 /* Generate a temporary name. */
23330 sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23331
23332 /*
23333 * Rename the existing partition with a temporary name, leaving it
23334 * free for the new partition. We don't need to care about this
23335 * in the future because we're going to eventually drop the
23336 * existing partition anyway.
23337 */
23339
23340 /*
23341 * We must bump the command counter to make the new partition
23342 * tuple visible for rename.
23343 */
23345 }
23346 else
23347 {
23348 ereport(ERROR,
23350 errmsg("relation \"%s\" already exists", cmd->name->relname));
23351 }
23352 }
23353
23356
23357 /*
23358 * Collect extension dependencies from indexes on the merging partitions.
23359 * We must do this before detaching them, so we can restore the
23360 * dependencies on the new partition's indexes later.
23361 */
23363
23364 /* Detach all merging partitions. */
23366 {
23368
23370
23372
23374 }
23375
23376 /*
23377 * Perform a preliminary check to determine whether it's safe to drop all
23378 * merging partitions before we actually do so later. After merging rows
23379 * into the new partitions via MergePartitionsMoveRows, all old partitions
23380 * need to be dropped. However, since the drop behavior is DROP_RESTRICT
23381 * and the merge process (MergePartitionsMoveRows) can be time-consuming,
23382 * performing an early check on the drop eligibility of old partitions is
23383 * preferable.
23384 */
23386 {
23387 ObjectAddress object;
23388
23389 /* Get oid of the later to be dropped relation. */
23391 object.classId = RelationRelationId;
23392 object.objectSubId = 0;
23393
23395 }
23396
23397 /*
23398 * Create a table for the new partition, using the partitioned table as a
23399 * model.
23400 */
23401 Assert(OidIsValid(ownerId));
23402 newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
23403
23404 /*
23405 * Switch to the table owner's userid, so that any index functions are run
23406 * as that user. Also, lockdown security-restricted operations and
23407 * arrange to make GUC variable changes local to this command.
23408 *
23409 * Need to do it after determining the namespace in the
23410 * createPartitionTable() call.
23411 */
23412 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23413 SetUserIdAndSecContext(ownerId,
23414 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23415 save_nestlevel = NewGUCNestLevel();
23417
23418 /* Copy data from merged partitions to the new partition. */
23420
23421 /* Drop the current partitions before attaching the new one. */
23423 {
23424 ObjectAddress object;
23425
23427 object.classId = RelationRelationId;
23428 object.objectSubId = 0;
23429
23430 performDeletion(&object, DROP_RESTRICT, 0);
23431 }
23432
23434
23435 /*
23436 * Attach a new partition to the partitioned table. wqueue = NULL:
23437 * verification for each cloned constraint is not needed.
23438 */
23440
23441 /*
23442 * Apply extension dependencies to the new partition's indexes. This
23443 * preserves any "DEPENDS ON EXTENSION" settings from the merged
23444 * partitions.
23445 */
23447
23449
23450 /* Keep the lock until commit. */
23452
23453 /* Roll back any GUC changes executed by index functions. */
23454 AtEOXact_GUC(false, save_nestlevel);
23455
23456 /* Restore the userid and security context. */
23457 SetUserIdAndSecContext(save_userid, save_sec_context);
23458}
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:331
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:262
List * partlist
Relation table_openrv_extended(const RangeVar *relation, LOCKMODE lockmode, bool missing_ok)
Definition table.c:103
static void MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
static 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 18754 of file tablecmds.c.

18755{
18756 Oid indexOid;
18757 Relation indexRel;
18758 int key;
18759
18760 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18761 {
18762 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18763 return;
18764 }
18765 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18766 {
18767 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18768 return;
18769 }
18770 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18771 {
18772 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18773 return;
18774 }
18775 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18776 {
18777 /* fallthrough */ ;
18778 }
18779 else
18780 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18781
18782 /* Check that the index exists */
18783 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18784 if (!OidIsValid(indexOid))
18785 ereport(ERROR,
18787 errmsg("index \"%s\" for table \"%s\" does not exist",
18788 stmt->name, RelationGetRelationName(rel))));
18789
18790 indexRel = index_open(indexOid, ShareLock);
18791
18792 /* Check that the index is on the relation we're altering. */
18793 if (indexRel->rd_index == NULL ||
18794 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18795 ereport(ERROR,
18797 errmsg("\"%s\" is not an index for table \"%s\"",
18798 RelationGetRelationName(indexRel),
18800
18801 /*
18802 * The AM must support uniqueness, and the index must in fact be unique.
18803 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
18804 * exclusion), we can use that too.
18805 */
18806 if ((!indexRel->rd_indam->amcanunique ||
18807 !indexRel->rd_index->indisunique) &&
18808 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
18809 ereport(ERROR,
18811 errmsg("cannot use non-unique index \"%s\" as replica identity",
18812 RelationGetRelationName(indexRel))));
18813 /* Deferred indexes are not guaranteed to be always unique. */
18814 if (!indexRel->rd_index->indimmediate)
18815 ereport(ERROR,
18817 errmsg("cannot use non-immediate index \"%s\" as replica identity",
18818 RelationGetRelationName(indexRel))));
18819 /* Expression indexes aren't supported. */
18820 if (RelationGetIndexExpressions(indexRel) != NIL)
18821 ereport(ERROR,
18823 errmsg("cannot use expression index \"%s\" as replica identity",
18824 RelationGetRelationName(indexRel))));
18825 /* Predicate indexes aren't supported. */
18826 if (RelationGetIndexPredicate(indexRel) != NIL)
18827 ereport(ERROR,
18829 errmsg("cannot use partial index \"%s\" as replica identity",
18830 RelationGetRelationName(indexRel))));
18831
18832 /* Check index for nullable columns. */
18833 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
18834 {
18835 int16 attno = indexRel->rd_index->indkey.values[key];
18836 Form_pg_attribute attr;
18837
18838 /*
18839 * Reject any other system columns. (Going forward, we'll disallow
18840 * indexes containing such columns in the first place, but they might
18841 * exist in older branches.)
18842 */
18843 if (attno <= 0)
18844 ereport(ERROR,
18846 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
18847 RelationGetRelationName(indexRel), attno)));
18848
18849 attr = TupleDescAttr(rel->rd_att, attno - 1);
18850 if (!attr->attnotnull)
18851 ereport(ERROR,
18853 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
18854 RelationGetRelationName(indexRel),
18855 NameStr(attr->attname))));
18856 }
18857
18858 /* This index is suitable for use as a replica identity. Mark it. */
18859 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
18860
18861 index_close(indexRel, NoLock);
18862}
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition rel.h:535
List * RelationGetIndexPredicate(Relation relation)
Definition relcache.c:5201
List * RelationGetIndexExpressions(Relation relation)
Definition relcache.c:5088
bool amcanunique
Definition amapi.h:258
const struct IndexAmRoutine * rd_indam
Definition rel.h:206
TupleDesc rd_att
Definition rel.h:112
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)

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

Referenced by ATExecCmd().

◆ ATExecSetAccessMethodNoStorage()

static void ATExecSetAccessMethodNoStorage ( Relation  rel,
Oid  newAccessMethodId 
)
static

Definition at line 16798 of file tablecmds.c.

16799{
16802 HeapTuple tuple;
16803 Form_pg_class rd_rel;
16804 Oid reloid = RelationGetRelid(rel);
16805
16806 /*
16807 * Shouldn't be called on relations having storage; these are processed in
16808 * phase 3.
16809 */
16810 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16811
16812 /* Get a modifiable copy of the relation's pg_class row. */
16814
16816 if (!HeapTupleIsValid(tuple))
16817 elog(ERROR, "cache lookup failed for relation %u", reloid);
16818 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16819
16820 /* Update the pg_class row. */
16821 oldAccessMethodId = rd_rel->relam;
16822 rd_rel->relam = newAccessMethodId;
16823
16824 /* Leave if no update required */
16825 if (rd_rel->relam == oldAccessMethodId)
16826 {
16827 heap_freetuple(tuple);
16829 return;
16830 }
16831
16832 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16833
16834 /*
16835 * Update the dependency on the new access method. No dependency is added
16836 * if the new access method is InvalidOid (default case). Be very careful
16837 * that this has to compare the previous value stored in pg_class with the
16838 * new one.
16839 */
16840 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16841 {
16843 referenced;
16844
16845 /*
16846 * New access method is defined and there was no dependency
16847 * previously, so record a new one.
16848 */
16852 }
16853 else if (OidIsValid(oldAccessMethodId) &&
16854 !OidIsValid(rd_rel->relam))
16855 {
16856 /*
16857 * There was an access method defined, and no new one, so just remove
16858 * the existing dependency.
16859 */
16863 }
16864 else
16865 {
16867 OidIsValid(rd_rel->relam));
16868
16869 /* Both are valid, so update the dependency */
16872 oldAccessMethodId, rd_rel->relam);
16873 }
16874
16875 /* make the relam and dependency changes visible */
16877
16879
16880 heap_freetuple(tuple);
16882}

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

19012{
19014 HeapTuple tuple;
19017 char *compression;
19018 char cmethod;
19019 ObjectAddress address;
19020
19021 compression = strVal(newValue);
19022
19024
19025 /* copy the cache entry so we can scribble on it below */
19027 if (!HeapTupleIsValid(tuple))
19028 ereport(ERROR,
19030 errmsg("column \"%s\" of relation \"%s\" does not exist",
19032
19033 /* prevent them from altering a system attribute */
19035 attnum = atttableform->attnum;
19036 if (attnum <= 0)
19037 ereport(ERROR,
19039 errmsg("cannot alter system column \"%s\"", column)));
19040
19041 /*
19042 * Check that column type is compressible, then get the attribute
19043 * compression method code
19044 */
19045 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
19046
19047 /* update pg_attribute entry */
19048 atttableform->attcompression = cmethod;
19049 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
19050
19052 RelationGetRelid(rel),
19053 attnum);
19054
19055 /*
19056 * Apply the change to indexes as well (only for simple index columns,
19057 * matching behavior of index.c ConstructTupleDescriptor()).
19058 */
19060 false, 0,
19061 true, cmethod,
19062 lockmode);
19063
19064 heap_freetuple(tuple);
19065
19067
19068 /* make changes visible */
19070
19072 RelationGetRelid(rel), attnum);
19073 return address;
19074}
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:9232

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

8719{
8720 HeapTuple tuple;
8723 char attgenerated;
8724 bool rewrite;
8726 ObjectAddress address;
8727 Expr *defval;
8730
8732 if (!HeapTupleIsValid(tuple))
8733 ereport(ERROR,
8735 errmsg("column \"%s\" of relation \"%s\" does not exist",
8737
8739
8740 attnum = attTup->attnum;
8741 if (attnum <= 0)
8742 ereport(ERROR,
8744 errmsg("cannot alter system column \"%s\"",
8745 colName)));
8746
8747 attgenerated = attTup->attgenerated;
8748 if (!attgenerated)
8749 ereport(ERROR,
8751 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8753
8754 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8755 tab->verify_new_notnull = true;
8756
8757 /*
8758 * We need to prevent this because a change of expression could affect a
8759 * row filter and inject expressions that are not permitted in a row
8760 * filter. XXX We could try to have a more precise check to catch only
8761 * publications with row filters, or even re-verify the row filter
8762 * expressions.
8763 */
8764 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8766 ereport(ERROR,
8768 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8769 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8771
8772 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8773
8774 ReleaseSysCache(tuple);
8775
8776 if (rewrite)
8777 {
8778 /*
8779 * Clear all the missing values if we're rewriting the table, since
8780 * this renders them pointless.
8781 */
8783
8784 /* make sure we don't conflict with later attribute modifications */
8786 }
8787
8788 /*
8789 * Find everything that depends on the column (constraints, indexes, etc),
8790 * and record enough information to let us recreate the objects.
8791 */
8793
8794 /*
8795 * Drop the dependency records of the GENERATED expression, in particular
8796 * its INTERNAL dependency on the column, which would otherwise cause
8797 * dependency.c to refuse to perform the deletion.
8798 */
8800 if (!OidIsValid(attrdefoid))
8801 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8802 RelationGetRelid(rel), attnum);
8804
8805 /* Make above changes visible */
8807
8808 /*
8809 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8810 * safety, but at present we do not expect anything to depend on the
8811 * expression.
8812 */
8814 false, false);
8815
8816 /* Prepare to store the new expression, in the catalogs */
8818 rawEnt->attnum = attnum;
8819 rawEnt->raw_default = newExpr;
8820 rawEnt->generated = attgenerated;
8821
8822 /* Store the generated expression */
8824 false, true, false, NULL);
8825
8826 /* Make above new expression visible */
8828
8829 if (rewrite)
8830 {
8831 /* Prepare for table rewrite */
8832 defval = (Expr *) build_column_default(rel, attnum);
8833
8835 newval->attnum = attnum;
8836 newval->expr = expression_planner(defval);
8837 newval->is_generated = true;
8838
8839 tab->newvals = lappend(tab->newvals, newval);
8841 }
8842
8843 /* Drop any pg_statistic entry for the column */
8845
8847 RelationGetRelid(rel), attnum);
8848
8850 RelationGetRelid(rel), attnum);
8851 return address;
8852}
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 8486 of file tablecmds.c.

8488{
8491 HeapTuple tuple;
8495 ObjectAddress address;
8496 bool ispartitioned;
8497
8498 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8499 if (ispartitioned && !recurse)
8500 ereport(ERROR,
8502 errmsg("cannot change identity column of only the partitioned table"),
8503 errhint("Do not specify the ONLY keyword.")));
8504
8505 if (rel->rd_rel->relispartition && !recursing)
8506 ereport(ERROR,
8508 errmsg("cannot change identity column of a partition"));
8509
8510 foreach(option, castNode(List, def))
8511 {
8513
8514 if (strcmp(defel->defname, "generated") == 0)
8515 {
8516 if (generatedEl)
8517 ereport(ERROR,
8519 errmsg("conflicting or redundant options")));
8521 }
8522 else
8523 elog(ERROR, "option \"%s\" not recognized",
8524 defel->defname);
8525 }
8526
8527 /*
8528 * Even if there is nothing to change here, we run all the checks. There
8529 * will be a subsequent ALTER SEQUENCE that relies on everything being
8530 * there.
8531 */
8532
8535 if (!HeapTupleIsValid(tuple))
8536 ereport(ERROR,
8538 errmsg("column \"%s\" of relation \"%s\" does not exist",
8540
8542 attnum = attTup->attnum;
8543
8544 if (attnum <= 0)
8545 ereport(ERROR,
8547 errmsg("cannot alter system column \"%s\"",
8548 colName)));
8549
8550 if (!attTup->attidentity)
8551 ereport(ERROR,
8553 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8555
8556 if (generatedEl)
8557 {
8558 attTup->attidentity = defGetInt32(generatedEl);
8559 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8560
8562 RelationGetRelid(rel),
8563 attTup->attnum);
8565 RelationGetRelid(rel), attnum);
8566 }
8567 else
8568 address = InvalidObjectAddress;
8569
8570 heap_freetuple(tuple);
8572
8573 /*
8574 * Recurse to propagate the identity change to partitions. Identity is not
8575 * inherited in regular inheritance children.
8576 */
8577 if (generatedEl && recurse && ispartitioned)
8578 {
8579 List *children;
8580 ListCell *lc;
8581
8582 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8583
8584 foreach(lc, children)
8585 {
8587
8589 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8591 }
8592 }
8593
8594 return address;
8595}
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 8028 of file tablecmds.c.

8030{
8031 HeapTuple tuple;
8033 ObjectAddress address;
8034 Constraint *constraint;
8036 List *cooked;
8037 bool is_no_inherit = false;
8038
8039 /* Guard against stack overflow due to overly deep inheritance tree. */
8041
8042 /* At top level, permission check was done in ATPrepCmd, else do it */
8043 if (recursing)
8044 {
8047 Assert(conName != NULL);
8048 }
8049
8052 ereport(ERROR,
8054 errmsg("column \"%s\" of relation \"%s\" does not exist",
8056
8057 /* Prevent them from altering a system attribute */
8058 if (attnum <= 0)
8059 ereport(ERROR,
8061 errmsg("cannot alter system column \"%s\"",
8062 colName)));
8063
8064 /* See if there's already a constraint */
8066 if (HeapTupleIsValid(tuple))
8067 {
8069 bool changed = false;
8070
8071 /*
8072 * Don't let a NO INHERIT constraint be changed into inherit.
8073 */
8074 if (conForm->connoinherit && recurse)
8075 ereport(ERROR,
8077 errmsg("cannot change NO INHERIT status of NOT NULL constraint \"%s\" on relation \"%s\"",
8078 NameStr(conForm->conname),
8080
8081 /*
8082 * If we find an appropriate constraint, we're almost done, but just
8083 * need to change some properties on it: if we're recursing, increment
8084 * coninhcount; if not, set conislocal if not already set.
8085 */
8086 if (recursing)
8087 {
8088 if (pg_add_s16_overflow(conForm->coninhcount, 1,
8089 &conForm->coninhcount))
8090 ereport(ERROR,
8092 errmsg("too many inheritance parents"));
8093 changed = true;
8094 }
8095 else if (!conForm->conislocal)
8096 {
8097 conForm->conislocal = true;
8098 changed = true;
8099 }
8100 else if (!conForm->convalidated)
8101 {
8102 /*
8103 * Flip attnotnull and convalidated, and also validate the
8104 * constraint.
8105 */
8106 return ATExecValidateConstraint(wqueue, rel, NameStr(conForm->conname),
8107 recurse, recursing, lockmode);
8108 }
8109
8110 if (changed)
8111 {
8113
8115
8116 CatalogTupleUpdate(constr_rel, &tuple->t_self, tuple);
8119 }
8120
8121 if (changed)
8122 return address;
8123 else
8124 return InvalidObjectAddress;
8125 }
8126
8127 /*
8128 * If we're asked not to recurse, and children exist, raise an error for
8129 * partitioned tables. For inheritance, we act as if NO INHERIT had been
8130 * specified.
8131 */
8132 if (!recurse &&
8134 NoLock) != NIL)
8135 {
8136 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
8137 ereport(ERROR,
8139 errmsg("constraint must be added to child tables too"),
8140 errhint("Do not specify the ONLY keyword."));
8141 else
8142 is_no_inherit = true;
8143 }
8144
8145 /*
8146 * No constraint exists; we must add one. First determine a name to use,
8147 * if we haven't already.
8148 */
8149 if (!recursing)
8150 {
8151 Assert(conName == NULL);
8153 colName, "not_null",
8155 NIL);
8156 }
8157
8159 constraint->is_no_inherit = is_no_inherit;
8160 constraint->conname = conName;
8161
8162 /* and do it */
8163 cooked = AddRelationNewConstraints(rel, NIL, list_make1(constraint),
8164 false, !recursing, false, NULL);
8165 ccon = linitial(cooked);
8166 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
8167
8168 /* Mark pg_attribute.attnotnull for the column and queue validation */
8169 set_attnotnull(wqueue, rel, attnum, true, true);
8170
8172 RelationGetRelid(rel), attnum);
8173
8174 /*
8175 * Recurse to propagate the constraint to children that don't have one.
8176 */
8177 if (recurse)
8178 {
8179 List *children;
8180
8182 lockmode);
8183
8184 foreach_oid(childoid, children)
8185 {
8187
8189
8191 recurse, true, lockmode);
8193 }
8194 }
8195
8196 return address;
8197}
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 9153 of file tablecmds.c.

9155{
9157 HeapTuple tuple,
9158 newtuple;
9161 Datum datum,
9162 newOptions;
9163 bool isnull;
9164 ObjectAddress address;
9168
9170
9172
9173 if (!HeapTupleIsValid(tuple))
9174 ereport(ERROR,
9176 errmsg("column \"%s\" of relation \"%s\" does not exist",
9179
9180 attnum = attrtuple->attnum;
9181 if (attnum <= 0)
9182 ereport(ERROR,
9184 errmsg("cannot alter system column \"%s\"",
9185 colName)));
9186
9187 /* Generate new proposed attoptions (text array) */
9189 &isnull);
9190 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9192 false, isReset);
9193 /* Validate new options */
9195
9196 /* Build new tuple. */
9197 memset(repl_null, false, sizeof(repl_null));
9198 memset(repl_repl, false, sizeof(repl_repl));
9199 if (newOptions != (Datum) 0)
9201 else
9206
9207 /* Update system catalog. */
9208 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9209
9211 RelationGetRelid(rel),
9212 attrtuple->attnum);
9214 RelationGetRelid(rel), attnum);
9215
9216 heap_freetuple(newtuple);
9217
9218 ReleaseSysCache(tuple);
9219
9221
9222 return address;
9223}
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 16918 of file tablecmds.c.

16920{
16921 Oid relid;
16923 HeapTuple tuple;
16924 HeapTuple newtuple;
16925 Datum datum;
16930 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16931
16933 return; /* nothing to do */
16934
16936
16937 /* Fetch heap tuple */
16938 relid = RelationGetRelid(rel);
16940 if (!HeapTupleIsValid(tuple))
16941 elog(ERROR, "cache lookup failed for relation %u", relid);
16942
16944 {
16945 /*
16946 * If we're supposed to replace the reloptions list, we just pretend
16947 * there were none before.
16948 */
16949 datum = (Datum) 0;
16950 }
16951 else
16952 {
16953 bool isnull;
16954
16955 /* Get the old reloptions */
16957 &isnull);
16958 if (isnull)
16959 datum = (Datum) 0;
16960 }
16961
16962 /* Generate new proposed reloptions (text array) */
16965
16966 /* Validate */
16967 switch (rel->rd_rel->relkind)
16968 {
16969 case RELKIND_RELATION:
16970 case RELKIND_MATVIEW:
16971 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16972 break;
16975 break;
16976 case RELKIND_VIEW:
16978 break;
16979 case RELKIND_INDEX:
16982 break;
16983 case RELKIND_TOASTVALUE:
16984 /* fall through to error -- shouldn't ever get here */
16985 default:
16986 ereport(ERROR,
16988 errmsg("cannot set options for relation \"%s\"",
16990 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16991 break;
16992 }
16993
16994 /* Special-case validation of view options */
16995 if (rel->rd_rel->relkind == RELKIND_VIEW)
16996 {
16999 ListCell *cell;
17000 bool check_option = false;
17001
17002 foreach(cell, view_options)
17003 {
17004 DefElem *defel = (DefElem *) lfirst(cell);
17005
17006 if (strcmp(defel->defname, "check_option") == 0)
17007 check_option = true;
17008 }
17009
17010 /*
17011 * If the check option is specified, look to see if the view is
17012 * actually auto-updatable or not.
17013 */
17014 if (check_option)
17015 {
17016 const char *view_updatable_error =
17018
17020 ereport(ERROR,
17022 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
17023 errhint("%s", _(view_updatable_error))));
17024 }
17025 }
17026
17027 /*
17028 * All we need do here is update the pg_class row; the new options will be
17029 * propagated into relcaches during post-commit cache inval.
17030 */
17031 memset(repl_val, 0, sizeof(repl_val));
17032 memset(repl_null, false, sizeof(repl_null));
17033 memset(repl_repl, false, sizeof(repl_repl));
17034
17035 if (newOptions != (Datum) 0)
17037 else
17039
17041
17042 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17044
17045 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17047
17049
17050 heap_freetuple(newtuple);
17051
17052 ReleaseSysCache(tuple);
17053
17054 /* repeat the whole exercise for the toast table, if there's one */
17055 if (OidIsValid(rel->rd_rel->reltoastrelid))
17056 {
17058 Oid toastid = rel->rd_rel->reltoastrelid;
17059
17060 toastrel = table_open(toastid, lockmode);
17061
17062 /* Fetch heap tuple */
17064 if (!HeapTupleIsValid(tuple))
17065 elog(ERROR, "cache lookup failed for relation %u", toastid);
17066
17068 {
17069 /*
17070 * If we're supposed to replace the reloptions list, we just
17071 * pretend there were none before.
17072 */
17073 datum = (Datum) 0;
17074 }
17075 else
17076 {
17077 bool isnull;
17078
17079 /* Get the old reloptions */
17081 &isnull);
17082 if (isnull)
17083 datum = (Datum) 0;
17084 }
17085
17087 false, operation == AT_ResetRelOptions);
17088
17090
17091 memset(repl_val, 0, sizeof(repl_val));
17092 memset(repl_null, false, sizeof(repl_null));
17093 memset(repl_repl, false, sizeof(repl_repl));
17094
17095 if (newOptions != (Datum) 0)
17097 else
17099
17101
17102 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17104
17105 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17106
17109 InvalidOid, true);
17110
17111 heap_freetuple(newtuple);
17112
17113 ReleaseSysCache(tuple);
17114
17116 }
17117
17119}
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:304
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 18868 of file tablecmds.c.

18869{
18871 Oid relid;
18872 HeapTuple tuple;
18873
18874 relid = RelationGetRelid(rel);
18875
18876 /* Pull the record for this relation and update it */
18878
18880
18881 if (!HeapTupleIsValid(tuple))
18882 elog(ERROR, "cache lookup failed for relation %u", relid);
18883
18885 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18886
18888 RelationGetRelid(rel), 0);
18889
18891 heap_freetuple(tuple);
18892}

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

9009{
9010 int newtarget = 0;
9011 bool newtarget_default;
9013 HeapTuple tuple,
9014 newtuple;
9017 ObjectAddress address;
9021
9022 /*
9023 * We allow referencing columns by numbers only for indexes, since table
9024 * column numbers could contain gaps if columns are later dropped.
9025 */
9026 if (rel->rd_rel->relkind != RELKIND_INDEX &&
9027 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
9028 !colName)
9029 ereport(ERROR,
9031 errmsg("cannot refer to non-index column by number")));
9032
9033 /* -1 was used in previous versions for the default setting */
9034 if (newValue && intVal(newValue) != -1)
9035 {
9037 newtarget_default = false;
9038 }
9039 else
9040 newtarget_default = true;
9041
9042 if (!newtarget_default)
9043 {
9044 /*
9045 * Limit target to a sane range
9046 */
9047 if (newtarget < 0)
9048 {
9049 ereport(ERROR,
9051 errmsg("statistics target %d is too low",
9052 newtarget)));
9053 }
9055 {
9059 errmsg("lowering statistics target to %d",
9060 newtarget)));
9061 }
9062 }
9063
9065
9066 if (colName)
9067 {
9069
9070 if (!HeapTupleIsValid(tuple))
9071 ereport(ERROR,
9073 errmsg("column \"%s\" of relation \"%s\" does not exist",
9075 }
9076 else
9077 {
9079
9080 if (!HeapTupleIsValid(tuple))
9081 ereport(ERROR,
9083 errmsg("column number %d of relation \"%s\" does not exist",
9085 }
9086
9088
9089 attnum = attrtuple->attnum;
9090 if (attnum <= 0)
9091 ereport(ERROR,
9093 errmsg("cannot alter system column \"%s\"",
9094 colName)));
9095
9096 /*
9097 * Prevent this as long as the ANALYZE code skips virtual generated
9098 * columns.
9099 */
9100 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
9101 ereport(ERROR,
9103 errmsg("cannot alter statistics on virtual generated column \"%s\"",
9104 colName)));
9105
9106 if (rel->rd_rel->relkind == RELKIND_INDEX ||
9107 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
9108 {
9109 if (attnum > rel->rd_index->indnkeyatts)
9110 ereport(ERROR,
9112 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9113 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9114 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9115 ereport(ERROR,
9117 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9118 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9119 errhint("Alter statistics on table column instead.")));
9120 }
9121
9122 /* Build new tuple. */
9123 memset(repl_null, false, sizeof(repl_null));
9124 memset(repl_repl, false, sizeof(repl_repl));
9125 if (!newtarget_default)
9127 else
9132 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9133
9135 RelationGetRelid(rel),
9136 attrtuple->attnum);
9138 RelationGetRelid(rel), attnum);
9139
9140 heap_freetuple(newtuple);
9141
9142 ReleaseSysCache(tuple);
9143
9145
9146 return address;
9147}
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 9295 of file tablecmds.c.

9296{
9298 HeapTuple tuple;
9301 ObjectAddress address;
9302
9304
9306
9307 if (!HeapTupleIsValid(tuple))
9308 ereport(ERROR,
9310 errmsg("column \"%s\" of relation \"%s\" does not exist",
9313
9314 attnum = attrtuple->attnum;
9315 if (attnum <= 0)
9316 ereport(ERROR,
9318 errmsg("cannot alter system column \"%s\"",
9319 colName)));
9320
9321 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9322
9323 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9324
9326 RelationGetRelid(rel),
9327 attrtuple->attnum);
9328
9329 /*
9330 * Apply the change to indexes as well (only for simple index columns,
9331 * matching behavior of index.c ConstructTupleDescriptor()).
9332 */
9334 true, attrtuple->attstorage,
9335 false, 0,
9336 lockmode);
9337
9338 heap_freetuple(tuple);
9339
9341
9343 RelationGetRelid(rel), attnum);
9344 return address;
9345}
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 17126 of file tablecmds.c.

17127{
17128 Relation rel;
17133 ListCell *lc;
17134
17135 /*
17136 * Need lock here in case we are recursing to toast table or index
17137 */
17138 rel = relation_open(tableOid, lockmode);
17139
17140 /* Check first if relation can be moved to new tablespace */
17141 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
17142 {
17144 RelationGetRelid(rel), 0);
17145 relation_close(rel, NoLock);
17146 return;
17147 }
17148
17149 reltoastrelid = rel->rd_rel->reltoastrelid;
17150 /* Fetch the list of indexes on toast relation if necessary */
17152 {
17154
17156 relation_close(toastRel, lockmode);
17157 }
17158
17159 /*
17160 * Relfilenumbers are not unique in databases across tablespaces, so we
17161 * need to allocate a new one in the new tablespace.
17162 */
17163 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
17164 rel->rd_rel->relpersistence);
17165
17166 /* Open old and new relation */
17167 newrlocator = rel->rd_locator;
17169 newrlocator.spcOid = newTableSpace;
17170
17171 /* hand off to AM to actually create new rel storage and copy the data */
17172 if (rel->rd_rel->relkind == RELKIND_INDEX)
17173 {
17175 }
17176 else
17177 {
17178 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
17180 }
17181
17182 /*
17183 * Update the pg_class row.
17184 *
17185 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
17186 * executed on pg_class or its indexes (the above copy wouldn't contain
17187 * the updated pg_class entry), but that's forbidden with
17188 * CheckRelationTableSpaceMove().
17189 */
17190 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
17191
17193
17195
17196 relation_close(rel, NoLock);
17197
17198 /* Make sure the reltablespace change is visible */
17200
17201 /* Move associated toast relation and/or indexes, too */
17203 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
17204 foreach(lc, reltoastidxids)
17205 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
17206
17207 /* Clean up */
17209}
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition catalog.c:557
void RelationAssumeNewRelfilelocator(Relation relation)
Definition relcache.c:3968
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 17219 of file tablecmds.c.

17220{
17221 /*
17222 * Shouldn't be called on relations having storage; these are processed in
17223 * phase 3.
17224 */
17225 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
17226
17227 /* check if relation can be moved to its new tablespace */
17228 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
17229 {
17231 RelationGetRelid(rel),
17232 0);
17233 return;
17234 }
17235
17236 /* Update can be done, so change reltablespace */
17237 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
17238
17240
17241 /* Make sure the reltablespace change is visible */
17243}

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

23736{
23740 *listptr2;
23741 bool isSameName = false;
23742 char tmpRelName[NAMEDATALEN];
23743 List *newPartRels = NIL;
23744 List *extDepState = NIL;
23745 ObjectAddress object;
23747 Oid save_userid;
23748 int save_sec_context;
23749 int save_nestlevel;
23751
23753
23754 /*
23755 * Partition is already locked in the transformPartitionCmdForSplit
23756 * function.
23757 */
23759
23761
23762 /* Check descriptions of new partitions. */
23764 {
23766
23767 /* Look up the existing relation by the new partition name. */
23769
23770 /*
23771 * This would fail later on anyway if the relation already exists. But
23772 * by catching it here, we can emit a nicer error message.
23773 */
23775 /* One new partition can have the same name as a split partition. */
23776 isSameName = true;
23777 else if (OidIsValid(existingRelid))
23778 ereport(ERROR,
23780 errmsg("relation \"%s\" already exists", sps->name->relname));
23781 }
23782
23783 /*
23784 * Collect extension dependencies from indexes on the split partition. We
23785 * must do this before detaching it, so we can restore the dependencies on
23786 * the new partitions' indexes later.
23787 */
23789
23792
23793 /* Detach the split partition. */
23795
23796 /*
23797 * Perform a preliminary check to determine whether it's safe to drop the
23798 * split partition before we actually do so later. After merging rows into
23799 * the new partitions via SplitPartitionMoveRows, all old partitions need
23800 * to be dropped. However, since the drop behavior is DROP_RESTRICT and
23801 * the merge process (SplitPartitionMoveRows) can be time-consuming,
23802 * performing an early check on the drop eligibility of old partitions is
23803 * preferable.
23804 */
23805 object.objectId = splitRelOid;
23806 object.classId = RelationRelationId;
23807 object.objectSubId = 0;
23809
23810 /*
23811 * If a new partition has the same name as the split partition, then we
23812 * should rename the split partition to reuse its name.
23813 */
23814 if (isSameName)
23815 {
23816 /*
23817 * We must bump the command counter to make the split partition tuple
23818 * visible for renaming.
23819 */
23821 /* Rename partition. */
23822 sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23824
23825 /*
23826 * We must bump the command counter to make the split partition tuple
23827 * visible after renaming.
23828 */
23830 }
23831
23832 /* Create new partitions (like a split partition), without indexes. */
23834 {
23836
23838 splitRel->rd_rel->relowner);
23840 }
23841
23842 /*
23843 * Switch to the table owner's userid, so that any index functions are run
23844 * as that user. Also, lockdown security-restricted operations and
23845 * arrange to make GUC variable changes local to this command.
23846 *
23847 * Need to do it after determining the namespace in the
23848 * createPartitionTable() call.
23849 */
23850 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23851 SetUserIdAndSecContext(splitRel->rd_rel->relowner,
23852 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23853 save_nestlevel = NewGUCNestLevel();
23855
23856 /* Copy data from the split partition to the new partitions. */
23858 /* Keep the lock until commit. */
23860
23861 /* Attach new partitions to the partitioned table. */
23863 {
23866
23867 /*
23868 * wqueue = NULL: verification for each cloned constraint is not
23869 * needed.
23870 */
23871 attachPartitionTable(NULL, rel, newPartRel, sps->bound);
23872
23873 /*
23874 * Apply extension dependencies to the new partition's indexes. This
23875 * preserves any "DEPENDS ON EXTENSION" settings from the split
23876 * partition.
23877 */
23879
23880 /* Keep the lock until commit. */
23882 }
23883
23885
23886 /* Drop the split partition. */
23887 object.classId = RelationRelationId;
23888 object.objectId = splitRelOid;
23889 object.objectSubId = 0;
23890 /* Probably DROP_CASCADE is not needed. */
23891 performDeletion(&object, DROP_RESTRICT, 0);
23892
23893 /* Roll back any GUC changes executed by index functions. */
23894 AtEOXact_GUC(false, save_nestlevel);
23895
23896 /* Restore the userid and security context. */
23897 SetUserIdAndSecContext(save_userid, save_sec_context);
23898}
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 13180 of file tablecmds.c.

13182{
13184 SysScanDesc scan;
13185 ScanKeyData skey[3];
13186 HeapTuple tuple;
13188 ObjectAddress address;
13189
13191
13192 /*
13193 * Find and check the target constraint
13194 */
13195 ScanKeyInit(&skey[0],
13199 ScanKeyInit(&skey[1],
13203 ScanKeyInit(&skey[2],
13208 true, NULL, 3, skey);
13209
13210 /* There can be at most one matching row */
13211 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
13212 ereport(ERROR,
13214 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13216
13217 con = (Form_pg_constraint) GETSTRUCT(tuple);
13218 if (con->contype != CONSTRAINT_FOREIGN &&
13219 con->contype != CONSTRAINT_CHECK &&
13220 con->contype != CONSTRAINT_NOTNULL)
13221 ereport(ERROR,
13223 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
13225 errdetail("This operation is not supported for this type of constraint."));
13226
13227 if (!con->conenforced)
13228 ereport(ERROR,
13230 errmsg("cannot validate NOT ENFORCED constraint")));
13231
13232 if (!con->convalidated)
13233 {
13234 if (con->contype == CONSTRAINT_FOREIGN)
13235 {
13236 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
13237 tuple, lockmode);
13238 }
13239 else if (con->contype == CONSTRAINT_CHECK)
13240 {
13242 tuple, recurse, recursing, lockmode);
13243 }
13244 else if (con->contype == CONSTRAINT_NOTNULL)
13245 {
13247 tuple, recurse, recursing, lockmode);
13248 }
13249
13250 ObjectAddressSet(address, ConstraintRelationId, con->oid);
13251 }
13252 else
13253 address = InvalidObjectAddress; /* already validated */
13254
13255 systable_endscan(scan);
13256
13258
13259 return address;
13260}
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 15708 of file tablecmds.c.

15709{
15710 ObjectAddress obj;
15711 ObjectAddresses *objects;
15714
15715 /*
15716 * Collect all the constraints and indexes to drop so we can process them
15717 * in a single call. That way we don't have to worry about dependencies
15718 * among them.
15719 */
15720 objects = new_object_addresses();
15721
15722 /*
15723 * Re-parse the index and constraint definitions, and attach them to the
15724 * appropriate work queue entries. We do this before dropping because in
15725 * the case of a constraint on another table, we might not yet have
15726 * exclusive lock on the table the constraint is attached to, and we need
15727 * to get that before reparsing/dropping. (That's possible at least for
15728 * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15729 * requires a dependency on the target table's composite type in the other
15730 * table's constraint expressions.)
15731 *
15732 * We can't rely on the output of deparsing to tell us which relation to
15733 * operate on, because concurrent activity might have made the name
15734 * resolve differently. Instead, we've got to use the OID of the
15735 * constraint or index we're processing to figure out which relation to
15736 * operate on.
15737 */
15740 {
15742 HeapTuple tup;
15744 Oid relid;
15745 Oid confrelid;
15746 bool conislocal;
15747
15749 if (!HeapTupleIsValid(tup)) /* should not happen */
15750 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15752 if (OidIsValid(con->conrelid))
15753 relid = con->conrelid;
15754 else
15755 {
15756 /* must be a domain constraint */
15757 relid = get_typ_typrelid(getBaseType(con->contypid));
15758 if (!OidIsValid(relid))
15759 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15760 }
15761 confrelid = con->confrelid;
15762 conislocal = con->conislocal;
15764
15766 add_exact_object_address(&obj, objects);
15767
15768 /*
15769 * If the constraint is inherited (only), we don't want to inject a
15770 * new definition here; it'll get recreated when
15771 * ATAddCheckNNConstraint recurses from adding the parent table's
15772 * constraint. But we had to carry the info this far so that we can
15773 * drop the constraint below.
15774 */
15775 if (!conislocal)
15776 continue;
15777
15778 /*
15779 * When rebuilding another table's constraint that references the
15780 * table we're modifying, we might not yet have any lock on the other
15781 * table, so get one now. We'll need AccessExclusiveLock for the DROP
15782 * CONSTRAINT step, so there's no value in asking for anything weaker.
15783 */
15784 if (relid != tab->relid)
15786
15787 ATPostAlterTypeParse(oldId, relid, confrelid,
15788 (char *) lfirst(def_item),
15789 wqueue, lockmode, tab->rewrite);
15790 }
15793 {
15795 Oid relid;
15796
15797 relid = IndexGetRelation(oldId, false);
15798
15799 /*
15800 * As above, make sure we have lock on the index's table if it's not
15801 * the same table.
15802 */
15803 if (relid != tab->relid)
15805
15807 (char *) lfirst(def_item),
15808 wqueue, lockmode, tab->rewrite);
15809
15811 add_exact_object_address(&obj, objects);
15812 }
15813
15814 /* add dependencies for new statistics */
15817 {
15819 Oid relid;
15820
15821 relid = StatisticsGetRelation(oldId, false);
15822
15823 /*
15824 * As above, make sure we have lock on the statistics object's table
15825 * if it's not the same table. However, we take
15826 * ShareUpdateExclusiveLock here, aligning with the lock level used in
15827 * CreateStatistics and RemoveStatisticsById.
15828 *
15829 * CAUTION: this should be done after all cases that grab
15830 * AccessExclusiveLock, else we risk causing deadlock due to needing
15831 * to promote our table lock.
15832 */
15833 if (relid != tab->relid)
15835
15837 (char *) lfirst(def_item),
15838 wqueue, lockmode, tab->rewrite);
15839
15841 add_exact_object_address(&obj, objects);
15842 }
15843
15844 /*
15845 * Queue up command to restore replica identity index marking
15846 */
15847 if (tab->replicaIdentityIndex)
15848 {
15851
15852 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
15853 subcmd->name = tab->replicaIdentityIndex;
15855 cmd->def = (Node *) subcmd;
15856
15857 /* do it after indexes and constraints */
15860 }
15861
15862 /*
15863 * Queue up command to restore marking of index used for cluster.
15864 */
15865 if (tab->clusterOnIndex)
15866 {
15868
15869 cmd->subtype = AT_ClusterOn;
15870 cmd->name = tab->clusterOnIndex;
15871
15872 /* do it after indexes and constraints */
15875 }
15876
15877 /*
15878 * It should be okay to use DROP_RESTRICT here, since nothing else should
15879 * be depending on these objects.
15880 */
15882
15883 free_object_addresses(objects);
15884
15885 /*
15886 * The objects will get recreated during subsequent passes over the work
15887 * queue.
15888 */
15889}
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition index.c:3604
Oid get_typ_typrelid(Oid typid)
Definition lsyscache.c:2926
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 15900 of file tablecmds.c.

15902{
15906 Relation rel;
15907
15908 /*
15909 * We expect that we will get only ALTER TABLE and CREATE INDEX
15910 * statements. Hence, there is no need to pass them through
15911 * parse_analyze_*() or the rewriter, but instead we need to pass them
15912 * through parse_utilcmd.c to make them ready for execution.
15913 */
15917 {
15919 Node *stmt = rs->stmt;
15920
15921 if (IsA(stmt, IndexStmt))
15924 (IndexStmt *) stmt,
15925 cmd));
15926 else if (IsA(stmt, AlterTableStmt))
15927 {
15929 List *afterStmts;
15930
15932 (AlterTableStmt *) stmt,
15933 cmd,
15934 &beforeStmts,
15935 &afterStmts);
15939 }
15940 else if (IsA(stmt, CreateStatsStmt))
15944 cmd));
15945 else
15947 }
15948
15949 /* Caller should already have acquired whatever lock we need. */
15951
15952 /*
15953 * Attach each generated command to the proper place in the work queue.
15954 * Note this could result in creation of entirely new work-queue entries.
15955 *
15956 * Also note that we have to tweak the command subtypes, because it turns
15957 * out that re-creation of indexes and constraints has to act a bit
15958 * differently from initial creation.
15959 */
15960 foreach(list_item, querytree_list)
15961 {
15962 Node *stm = (Node *) lfirst(list_item);
15963 AlteredTableInfo *tab;
15964
15965 tab = ATGetQueueEntry(wqueue, rel);
15966
15967 if (IsA(stm, IndexStmt))
15968 {
15969 IndexStmt *stmt = (IndexStmt *) stm;
15971
15972 if (!rewrite)
15974 stmt->reset_default_tblspc = true;
15975 /* keep the index's comment */
15976 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15977
15979 newcmd->subtype = AT_ReAddIndex;
15980 newcmd->def = (Node *) stmt;
15983 }
15984 else if (IsA(stm, AlterTableStmt))
15985 {
15987 ListCell *lcmd;
15988
15989 foreach(lcmd, stmt->cmds)
15990 {
15992
15993 if (cmd->subtype == AT_AddIndex)
15994 {
15996 Oid indoid;
15997
15998 indstmt = castNode(IndexStmt, cmd->def);
15999 indoid = get_constraint_index(oldId);
16000
16001 if (!rewrite)
16002 TryReuseIndex(indoid, indstmt);
16003 /* keep any comment on the index */
16004 indstmt->idxcomment = GetComment(indoid,
16006 indstmt->reset_default_tblspc = true;
16007
16008 cmd->subtype = AT_ReAddIndex;
16010 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
16011
16012 /* recreate any comment on the constraint */
16015 oldId,
16016 rel,
16017 NIL,
16018 indstmt->idxname);
16019 }
16020 else if (cmd->subtype == AT_AddConstraint)
16021 {
16022 Constraint *con = castNode(Constraint, cmd->def);
16023
16025 /* rewriting neither side of a FK */
16026 if (con->contype == CONSTR_FOREIGN &&
16027 !rewrite && tab->rewrite == 0)
16029 con->reset_default_tblspc = true;
16033
16034 /*
16035 * Recreate any comment on the constraint. If we have
16036 * recreated a primary key, then transformTableConstraint
16037 * has added an unnamed not-null constraint here; skip
16038 * this in that case.
16039 */
16040 if (con->conname)
16043 oldId,
16044 rel,
16045 NIL,
16046 con->conname);
16047 else
16048 Assert(con->contype == CONSTR_NOTNULL);
16049 }
16050 else
16051 elog(ERROR, "unexpected statement subtype: %d",
16052 (int) cmd->subtype);
16053 }
16054 }
16055 else if (IsA(stm, AlterDomainStmt))
16056 {
16058
16059 if (stmt->subtype == AD_AddConstraint)
16060 {
16061 Constraint *con = castNode(Constraint, stmt->def);
16063
16065 cmd->def = (Node *) stmt;
16068
16069 /* recreate any comment on the constraint */
16072 oldId,
16073 NULL,
16074 stmt->typeName,
16075 con->conname);
16076 }
16077 else
16078 elog(ERROR, "unexpected statement subtype: %d",
16079 (int) stmt->subtype);
16080 }
16081 else if (IsA(stm, CreateStatsStmt))
16082 {
16085
16086 /* keep the statistics object's comment */
16087 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
16088
16090 newcmd->subtype = AT_ReAddStatistics;
16091 newcmd->def = (Node *) stmt;
16092 tab->subcmds[AT_PASS_MISC] =
16094 }
16095 else
16096 elog(ERROR, "unexpected statement type: %d",
16097 (int) nodeTag(stm));
16098 }
16099
16100 relation_close(rel, NoLock);
16101}
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:1232
#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 9601 of file tablecmds.c.

9604{
9606 List *children = NIL;
9607 bool got_children = false;
9608
9610 if (pkconstr->contype != CONSTR_PRIMARY)
9611 return;
9612
9613 /* Verify that columns are not-null, or request that they be made so */
9615 {
9618 HeapTuple tuple;
9619
9620 /*
9621 * First check if a suitable constraint exists. If it does, we don't
9622 * need to request another one. We do need to bail out if it's not
9623 * valid, though.
9624 */
9626 if (tuple != NULL)
9627 {
9629
9630 /* All good with this one; don't request another */
9631 heap_freetuple(tuple);
9632 continue;
9633 }
9634 else if (!recurse)
9635 {
9636 /*
9637 * No constraint on this column. Asked not to recurse, we won't
9638 * create one here, but verify that all children have one.
9639 */
9640 if (!got_children)
9641 {
9643 lockmode);
9644 /* only search for children on the first time through */
9645 got_children = true;
9646 }
9647
9648 foreach_oid(childrelid, children)
9649 {
9650 HeapTuple tup;
9651
9653 if (!tup)
9654 ereport(ERROR,
9655 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9657 /* verify it's good enough */
9659 }
9660 }
9661
9662 /* This column is not already not-null, so add it to the queue */
9664
9666 newcmd->subtype = AT_AddConstraint;
9667 /* note we force recurse=true here; see above */
9668 newcmd->recurse = true;
9669 newcmd->def = (Node *) nnconstr;
9670
9671 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9672 }
9673}
Definition value.h:64
static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
Definition tablecmds.c:9680

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

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

17511{
17512 if (child_rel->rd_rel->reloftype)
17513 ereport(ERROR,
17515 errmsg("cannot change inheritance of typed table")));
17516
17517 if (child_rel->rd_rel->relispartition)
17518 ereport(ERROR,
17520 errmsg("cannot change inheritance of a partition")));
17521}

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

Referenced by ATPrepCmd().

◆ ATPrepChangePersistence()

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

Definition at line 19085 of file tablecmds.c.

19086{
19088 HeapTuple tuple;
19089 SysScanDesc scan;
19090 ScanKeyData skey[1];
19091
19092 /*
19093 * Disallow changing status for a temp table. Also verify whether we can
19094 * get away with doing nothing; in such cases we don't need to run the
19095 * checks below, either.
19096 */
19097 switch (rel->rd_rel->relpersistence)
19098 {
19100 ereport(ERROR,
19102 errmsg("cannot change logged status of table \"%s\" because it is temporary",
19104 errtable(rel)));
19105 break;
19107 if (toLogged)
19108 /* nothing to do */
19109 return;
19110 break;
19112 if (!toLogged)
19113 /* nothing to do */
19114 return;
19115 break;
19116 }
19117
19118 /*
19119 * Check that the table is not part of any publication when changing to
19120 * UNLOGGED, as UNLOGGED tables can't be published.
19121 */
19122 if (!toLogged &&
19124 ereport(ERROR,
19126 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
19128 errdetail("Unlogged relations cannot be replicated.")));
19129
19130 /*
19131 * Check existing foreign key constraints to preserve the invariant that
19132 * permanent tables cannot reference unlogged ones. Self-referencing
19133 * foreign keys can safely be ignored.
19134 */
19136
19137 /*
19138 * Scan conrelid if changing to permanent, else confrelid. This also
19139 * determines whether a useful index exists.
19140 */
19141 ScanKeyInit(&skey[0],
19148 true, NULL, 1, skey);
19149
19150 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19151 {
19153
19154 if (con->contype == CONSTRAINT_FOREIGN)
19155 {
19157 Relation foreignrel;
19158
19159 /* the opposite end of what we used as scankey */
19160 foreignrelid = toLogged ? con->confrelid : con->conrelid;
19161
19162 /* ignore if self-referencing */
19163 if (RelationGetRelid(rel) == foreignrelid)
19164 continue;
19165
19167
19168 if (toLogged)
19169 {
19170 if (!RelationIsPermanent(foreignrel))
19171 ereport(ERROR,
19173 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
19175 RelationGetRelationName(foreignrel)),
19176 errtableconstraint(rel, NameStr(con->conname))));
19177 }
19178 else
19179 {
19180 if (RelationIsPermanent(foreignrel))
19181 ereport(ERROR,
19183 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
19185 RelationGetRelationName(foreignrel)),
19186 errtableconstraint(rel, NameStr(con->conname))));
19187 }
19188
19189 relation_close(foreignrel, AccessShareLock);
19190 }
19191 }
19192
19193 systable_endscan(scan);
19194
19196
19197 /* force rewrite if necessary; see comment in ATRewriteTables */
19199 if (toLogged)
19201 else
19203 tab->chgPersistence = true;
19204}
#define AT_REWRITE_ALTER_PERSISTENCE
return true
Definition isn.c:130
int errtableconstraint(Relation rel, const char *conname)
Definition relcache.c:6117
int errtable(Relation rel)
Definition relcache.c:6063

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:8858
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:9358
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:9601
#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 9358 of file tablecmds.c.

9361{
9362 if (rel->rd_rel->reloftype && !recursing)
9363 ereport(ERROR,
9365 errmsg("cannot drop column from typed table")));
9366
9367 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9368 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9369
9370 if (recurse)
9371 cmd->recurse = true;
9372}

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

8859{
8860 /*
8861 * Reject ONLY if there are child tables. We could implement this, but it
8862 * is a bit complicated. GENERATED clauses must be attached to the column
8863 * definition and cannot be added later like DEFAULT, so if a child table
8864 * has a generation expression that the parent does not have, the child
8865 * column will necessarily be an attislocal column. So to implement ONLY
8866 * here, we'd need extra code to update attislocal of the direct child
8867 * tables, somewhat similar to how DROP COLUMN does it, so that the
8868 * resulting state can be properly dumped and restored.
8869 */
8870 if (!recurse &&
8872 ereport(ERROR,
8874 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8875
8876 /*
8877 * Cannot drop generation expression from inherited columns.
8878 */
8879 if (!recursing)
8880 {
8881 HeapTuple tuple;
8883
8885 if (!HeapTupleIsValid(tuple))
8886 ereport(ERROR,
8888 errmsg("column \"%s\" of relation \"%s\" does not exist",
8889 cmd->name, RelationGetRelationName(rel))));
8890
8892
8893 if (attTup->attinhcount > 0)
8894 ereport(ERROR,
8896 errmsg("cannot drop generation expression from inherited column")));
8897 }
8898}

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

16765{
16766 Oid amoid;
16767
16768 /*
16769 * Look up the access method name and check that it differs from the
16770 * table's current AM. If DEFAULT was specified for a partitioned table
16771 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16772 */
16773 if (amname != NULL)
16774 amoid = get_table_am_oid(amname, false);
16775 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16776 amoid = InvalidOid;
16777 else
16779
16780 /* if it's a match, phase 3 doesn't need to do anything */
16781 if (rel->rd_rel->relam == amoid)
16782 return;
16783
16784 /* Save info for Phase 3 to do the real work */
16786 tab->newAccessMethod = amoid;
16787 tab->chgAccessMethod = true;
16788}
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 16888 of file tablecmds.c.

16889{
16891
16892 /* Check that the tablespace exists */
16893 tablespaceId = get_tablespace_oid(tablespacename, false);
16894
16895 /* Check permissions except when moving to database's default */
16897 {
16899
16901 if (aclresult != ACLCHECK_OK)
16902 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16903 }
16904
16905 /* Save info for Phase 3 to do the real work */
16906 if (OidIsValid(tab->newTableSpace))
16907 ereport(ERROR,
16909 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16910
16912}

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
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition execExpr.c:786
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition execExpr.c:905
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:661
#define GetPerTupleMemoryContext(estate)
Definition executor.h:672
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:124
#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:6080
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:756
MemoryContext es_query_cxt
Definition execnodes.h:746
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:1009
#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:1864
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
Definition repack.c:1107
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 20903 of file tablecmds.c.

20904{
20905 List *idxes;
20909 ListCell *cell;
20910 MemoryContext cxt;
20912
20914 "AttachPartitionEnsureIndexes",
20917
20922
20923 /* Build arrays of all existing indexes and their IndexInfos */
20925 {
20927
20930 }
20931
20932 /*
20933 * If we're attaching a foreign table, we must fail if any of the indexes
20934 * is a constraint index; otherwise, there's nothing to do here. Do this
20935 * before starting work, to avoid wasting the effort of building a few
20936 * non-unique indexes before coming across a unique one.
20937 */
20938 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20939 {
20940 foreach(cell, idxes)
20941 {
20942 Oid idx = lfirst_oid(cell);
20944
20945 if (idxRel->rd_index->indisunique ||
20946 idxRel->rd_index->indisprimary)
20947 ereport(ERROR,
20949 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20952 errdetail("Partitioned table \"%s\" contains unique indexes.",
20955 }
20956
20957 goto out;
20958 }
20959
20960 /*
20961 * For each index on the partitioned table, find a matching one in the
20962 * partition-to-be; if one is not found, create one.
20963 */
20964 foreach(cell, idxes)
20965 {
20966 Oid idx = lfirst_oid(cell);
20968 IndexInfo *info;
20969 AttrMap *attmap;
20970 bool found = false;
20972
20973 /*
20974 * Ignore indexes in the partitioned table other than partitioned
20975 * indexes.
20976 */
20977 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20978 {
20980 continue;
20981 }
20982
20983 /* construct an indexinfo to compare existing indexes against */
20984 info = BuildIndexInfo(idxRel);
20986 RelationGetDescr(rel),
20987 false);
20989
20990 /*
20991 * Scan the list of existing indexes in the partition-to-be, and mark
20992 * the first matching, valid, unattached one we find, if any, as
20993 * partition of the parent index. If we find one, we're done.
20994 */
20995 for (int i = 0; i < list_length(attachRelIdxs); i++)
20996 {
20999
21000 /* does this index have a parent? if so, can't use it */
21001 if (attachrelIdxRels[i]->rd_rel->relispartition)
21002 continue;
21003
21004 /* If this index is invalid, can't use it */
21005 if (!attachrelIdxRels[i]->rd_index->indisvalid)
21006 continue;
21007
21008 if (CompareIndexInfo(attachInfos[i], info,
21009 attachrelIdxRels[i]->rd_indcollation,
21010 idxRel->rd_indcollation,
21011 attachrelIdxRels[i]->rd_opfamily,
21012 idxRel->rd_opfamily,
21013 attmap))
21014 {
21015 /*
21016 * If this index is being created in the parent because of a
21017 * constraint, then the child needs to have a constraint also,
21018 * so look for one. If there is no such constraint, this
21019 * index is no good, so keep looking.
21020 */
21022 {
21023 cldConstrOid =
21025 cldIdxId);
21026 /* no dice */
21028 continue;
21029
21030 /* Ensure they're both the same type of constraint */
21033 continue;
21034 }
21035
21036 /* bingo. */
21041 found = true;
21042
21044 break;
21045 }
21046 }
21047
21048 /*
21049 * If no suitable index was found in the partition-to-be, create one
21050 * now. Note that if this is a PK, not-null constraints must already
21051 * exist.
21052 */
21053 if (!found)
21054 {
21055 IndexStmt *stmt;
21056 Oid conOid;
21057
21059 idxRel, attmap,
21060 &conOid);
21064 conOid,
21065 -1,
21066 true, false, false, false, false);
21067 }
21068
21070 }
21071
21072out:
21073 /* Clean up. */
21074 for (int i = 0; i < list_length(attachRelIdxs); i++)
21078}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:262
char get_constraint_type(Oid conoid)
Definition lsyscache.c:1262
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
#define foreach_current_index(var_or_cell)
Definition pg_list.h: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 11893 of file tablecmds.c.

11900{
11905 bool queueValidation;
11909
11910 /* Fetch the parent constraint tuple */
11914 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11916 parentConstrIsEnforced = parentConstr->conenforced;
11917
11918 /* Fetch the child constraint tuple */
11922 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11924 partConstrFrelid = partConstr->confrelid;
11925 partConstrRelid = partConstr->conrelid;
11926
11927 /*
11928 * If the referenced table is partitioned, then the partition we're
11929 * attaching now has extra pg_constraint rows and action triggers that are
11930 * no longer needed. Remove those.
11931 */
11933 {
11935
11938
11940 }
11941
11942 /*
11943 * Will we need to validate this constraint? A valid parent constraint
11944 * implies that all child constraints have been validated, so if this one
11945 * isn't, we must trigger phase 3 validation.
11946 */
11947 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11948
11951
11952 /*
11953 * The action triggers in the new partition become redundant -- the parent
11954 * table already has equivalent ones, and those will be able to reach the
11955 * partition. Remove the ones in the partition. We identify them because
11956 * they have our constraint OID, as well as being on the referenced rel.
11957 */
11960
11963
11964 /*
11965 * Like the constraint, attach partition's "check" triggers to the
11966 * corresponding parent triggers if the constraint is ENFORCED. NOT
11967 * ENFORCED constraints do not have these triggers.
11968 */
11970 {
11973
11983 }
11984
11985 /*
11986 * We updated this pg_constraint row above to set its parent; validating
11987 * it will cause its convalidated flag to change, so we need CCI here. In
11988 * addition, we need it unconditionally for the rare case where the parent
11989 * table has *two* identical constraints; when reaching this function for
11990 * the second one, we must have made our changes visible, otherwise we
11991 * would try to attach both to this one.
11992 */
11994
11995 /* If validation is needed, put it in the queue now. */
11996 if (queueValidation)
11997 {
11999 Oid confrelid;
12000
12002
12005 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
12006
12007 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
12008
12009 /* Use the same lock as for AT_ValidateConstraint */
12014 }
12015}
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 20523 of file tablecmds.c.

20524{
20525 /*
20526 * Create an inheritance; the relevant checks are performed inside the
20527 * function.
20528 */
20529 CreateInheritance(attachrel, rel, true);
20530
20531 /* Update the pg_class entry. */
20532 StorePartitionBound(attachrel, rel, bound);
20533
20534 /* Ensure there exists a correct set of indexes in the partition. */
20536
20537 /* and triggers */
20539
20540 /*
20541 * Clone foreign key constraints. Callee is responsible for setting up
20542 * for phase 3 constraint verification.
20543 */
20545}
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition heap.c:4068
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:1084
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition tupdesc.c:900

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

22413{
22414 /*
22415 * Build the needed expression execution states. Here, we expect only NOT
22416 * NULL and CHECK constraint.
22417 */
22419 {
22420 switch (con->contype)
22421 {
22422 case CONSTR_CHECK:
22423
22424 /*
22425 * We already expanded virtual expression in
22426 * createTableConstraints.
22427 */
22428 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22429 break;
22430 case CONSTR_NOTNULL:
22431 /* Nothing to do here. */
22432 break;
22433 default:
22434 elog(ERROR, "unrecognized constraint type: %d",
22435 (int) con->contype);
22436 }
22437 }
22438
22439 /* Expression already planned in createTableConstraints */
22441 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22442}

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

16587{
16589 SysScanDesc scan;
16590 ScanKeyData key[1];
16592
16594 ScanKeyInit(&key[0],
16597 ObjectIdGetDatum(relationOid));
16599 true, NULL, 1, key);
16601 {
16606 Acl *newAcl;
16608 bool isNull;
16609 HeapTuple newtuple;
16610
16611 /* Ignore dropped columns */
16612 if (att->attisdropped)
16613 continue;
16614
16618 &isNull);
16619 /* Null ACLs do not require changes */
16620 if (isNull)
16621 continue;
16622
16623 memset(repl_null, false, sizeof(repl_null));
16624 memset(repl_repl, false, sizeof(repl_repl));
16625
16630
16634
16635 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16636
16637 heap_freetuple(newtuple);
16638 }
16639 systable_endscan(scan);
16641}

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

16652{
16654 SysScanDesc scan;
16655 ScanKeyData key[2];
16656 HeapTuple tup;
16657
16658 /*
16659 * SERIAL sequences are those having an auto dependency on one of the
16660 * table's columns (we don't care *which* column, exactly).
16661 */
16663
16664 ScanKeyInit(&key[0],
16668 ScanKeyInit(&key[1],
16671 ObjectIdGetDatum(relationOid));
16672 /* we leave refobjsubid unspecified */
16673
16675 NULL, 2, key);
16676
16677 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16678 {
16681
16682 /* skip dependencies other than auto dependencies on columns */
16683 if (depForm->refobjsubid == 0 ||
16684 depForm->classid != RelationRelationId ||
16685 depForm->objsubid != 0 ||
16686 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16687 continue;
16688
16689 /* Use relation_open just in case it's an index */
16690 seqRel = relation_open(depForm->objid, lockmode);
16691
16692 /* skip non-sequence relations */
16693 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16694 {
16695 /* No need to keep the lock */
16696 relation_close(seqRel, lockmode);
16697 continue;
16698 }
16699
16700 /* We don't need to close the sequence while we alter it. */
16701 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16702
16703 /* Now we can close it. Keep the lock till end of transaction. */
16705 }
16706
16707 systable_endscan(scan);
16708
16710}

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

7763{
7765 int attnum;
7766
7767 /*
7768 * this test is deliberately not attisdropped-aware, since if one tries to
7769 * add a column matching a dropped column name, it's gonna fail anyway.
7770 */
7773 PointerGetDatum(colname));
7775 return true;
7776
7779
7780 /*
7781 * We throw a different error message for conflicts with system column
7782 * names, since they are normally not shown and the user might otherwise
7783 * be confused about the reason for the conflict.
7784 */
7785 if (attnum <= 0)
7786 ereport(ERROR,
7788 errmsg("column name \"%s\" conflicts with a system column name",
7789 colname)));
7790 else
7791 {
7792 if (if_not_exists)
7793 {
7796 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7797 colname, RelationGetRelationName(rel))));
7798 return false;
7799 }
7800
7801 ereport(ERROR,
7803 errmsg("column \"%s\" of relation \"%s\" already exists",
7804 colname, RelationGetRelationName(rel))));
7805 }
7806
7807 return true;
7808}
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 13937 of file tablecmds.c.

13938{
13939 Oid roleid = GetUserId();
13941 int i;
13942
13943 /* Okay if we have relation-level REFERENCES permission */
13946 if (aclresult == ACLCHECK_OK)
13947 return;
13948 /* Else we must have REFERENCES on each column */
13949 for (i = 0; i < natts; i++)
13950 {
13952 roleid, ACL_REFERENCES);
13953 if (aclresult != ACLCHECK_OK)
13956 }
13957}
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition aclchk.c:3911
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4082
#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:6145

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

9974{
9975 char buf[NAMEDATALEN * 2];
9976 int buflen = 0;
9977 ListCell *lc;
9978
9979 buf[0] = '\0';
9980 foreach(lc, colnames)
9981 {
9982 const char *name = strVal(lfirst(lc));
9983
9984 if (buflen > 0)
9985 buf[buflen++] = '_'; /* insert _ between names */
9986
9987 /*
9988 * At this point we have buflen <= NAMEDATALEN. name should be less
9989 * than NAMEDATALEN already, but use strlcpy for paranoia.
9990 */
9991 strlcpy(buf + buflen, name, NAMEDATALEN);
9992 buflen += strlen(buf + buflen);
9993 if (buflen >= NAMEDATALEN)
9994 break;
9995 }
9996 return pstrdup(buf);
9997}
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 11349 of file tablecmds.c.

11350{
11352 AttrMap *attmap;
11353 ListCell *cell;
11354 SysScanDesc scan;
11355 ScanKeyData key[2];
11356 HeapTuple tuple;
11357 List *clone = NIL;
11359
11360 /*
11361 * Search for any constraints where this partition's parent is in the
11362 * referenced side. However, we must not clone any constraint whose
11363 * parent constraint is also going to be cloned, to avoid duplicates. So
11364 * do it in two steps: first construct the list of constraints to clone,
11365 * then go over that list cloning those whose parents are not in the list.
11366 * (We must not rely on the parent being seen first, since the catalog
11367 * scan could return children first.)
11368 */
11370 ScanKeyInit(&key[0],
11373 ScanKeyInit(&key[1],
11376 /* This is a seqscan, as we don't have a usable index ... */
11378 NULL, 2, key);
11379 while ((tuple = systable_getnext(scan)) != NULL)
11380 {
11382
11384 }
11385 systable_endscan(scan);
11387
11388 /*
11389 * Triggers of the foreign keys will be manipulated a bunch of times in
11390 * the loop below. To avoid repeatedly opening/closing the trigger
11391 * catalog relation, we open it here and pass it to the subroutines called
11392 * below.
11393 */
11395
11398 false);
11399 foreach(cell, clone)
11400 {
11401 Oid constrOid = lfirst_oid(cell);
11404 Oid indexOid;
11406 int numfks;
11413 int numfkdelsetcols;
11414 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11416 ObjectAddress address;
11419
11421 if (!HeapTupleIsValid(tuple))
11422 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11424
11425 /*
11426 * As explained above: don't try to clone a constraint for which we're
11427 * going to clone the parent.
11428 */
11429 if (list_member_oid(clone, constrForm->conparentid))
11430 {
11431 ReleaseSysCache(tuple);
11432 continue;
11433 }
11434
11435 /* We need the same lock level that CreateTrigger will acquire */
11437
11438 indexOid = constrForm->conindid;
11440 &numfks,
11441 conkey,
11442 confkey,
11443 conpfeqop,
11444 conppeqop,
11445 conffeqop,
11447 confdelsetcols);
11448
11449 for (int i = 0; i < numfks; i++)
11450 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11451
11454 fkconstraint->conname = NameStr(constrForm->conname);
11455 fkconstraint->deferrable = constrForm->condeferrable;
11456 fkconstraint->initdeferred = constrForm->condeferred;
11457 fkconstraint->location = -1;
11458 fkconstraint->pktable = NULL;
11459 /* ->fk_attrs determined below */
11460 fkconstraint->pk_attrs = NIL;
11461 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11462 fkconstraint->fk_upd_action = constrForm->confupdtype;
11463 fkconstraint->fk_del_action = constrForm->confdeltype;
11464 fkconstraint->fk_del_set_cols = NIL;
11465 fkconstraint->old_conpfeqop = NIL;
11466 fkconstraint->old_pktable_oid = InvalidOid;
11467 fkconstraint->is_enforced = constrForm->conenforced;
11468 fkconstraint->skip_validation = false;
11469 fkconstraint->initially_valid = constrForm->convalidated;
11470
11471 /* set up colnames that are used to generate the constraint name */
11472 for (int i = 0; i < numfks; i++)
11473 {
11475
11477 conkey[i] - 1);
11478 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11479 makeString(NameStr(att->attname)));
11480 }
11481
11482 /*
11483 * Add the new foreign key constraint pointing to the new partition.
11484 * Because this new partition appears in the referenced side of the
11485 * constraint, we don't need to set up for Phase 3 check.
11486 */
11488 if (!OidIsValid(partIndexId))
11489 elog(ERROR, "index for %u not found in partition %s",
11491
11492 /*
11493 * Get the "action" triggers belonging to the constraint to pass as
11494 * parent OIDs for similar triggers that will be created on the
11495 * partition in addFkRecurseReferenced().
11496 */
11497 if (constrForm->conenforced)
11499 constrForm->confrelid, constrForm->conrelid,
11501
11502 /* Add this constraint ... */
11504 fkconstraint->conname, fkconstraint, fkRel,
11508 numfkdelsetcols, confdelsetcols, false,
11509 constrForm->conperiod);
11510 /* ... and recurse */
11512 fkRel,
11515 address.objectId,
11516 numfks,
11518 conkey,
11519 conpfeqop,
11520 conppeqop,
11521 conffeqop,
11523 confdelsetcols,
11524 true,
11527 constrForm->conperiod);
11528
11530 ReleaseSysCache(tuple);
11531 }
11532
11534}
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 11550 of file tablecmds.c.

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

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

11322{
11323 /* This only works for declarative partitioning */
11324 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11325
11326 /*
11327 * First, clone constraints where the parent is on the referencing side.
11328 */
11330
11331 /*
11332 * Clone constraints for which the parent is on the referenced side.
11333 */
11335}
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 21086 of file tablecmds.c.

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

23068{
23071
23072 if (ea->parentIndexOid != eb->parentIndexOid)
23073 return pg_cmp_u32(ea->parentIndexOid, eb->parentIndexOid);
23074 return pg_cmp_u32(ea->indexOid, eb->indexOid);
23075}
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 23095 of file tablecmds.c.

23096{
23097 List *collected = NIL;
23098 List *result = NIL;
23100
23101 /*
23102 * Phase 1: collect one entry per (partition index -> parent index) pair,
23103 * with its extension dependency OIDs sorted ascending.
23104 */
23106 {
23107 Relation partRel;
23108 List *indexList;
23109
23110 /*
23111 * Use NoLock since the caller already holds AccessExclusiveLock on
23112 * these partitions.
23113 */
23114 partRel = table_open(partOid, NoLock);
23116
23117 foreach_oid(indexOid, indexList)
23118 {
23119 Oid parentIndexOid;
23121
23122 if (!get_rel_relispartition(indexOid))
23123 continue;
23124
23125 parentIndexOid = get_partition_parent(indexOid, true);
23126 if (!OidIsValid(parentIndexOid))
23127 continue;
23128
23129 entry = palloc(sizeof(PartitionIndexExtDepEntry));
23130 entry->parentIndexOid = parentIndexOid;
23131 entry->indexOid = indexOid;
23133 indexOid);
23135
23136 collected = lappend(collected, entry);
23137 }
23138
23140 table_close(partRel, NoLock);
23141 }
23142
23143 /*
23144 * Phase 2: sort by parentIndexOid so entries sharing a parent index sit
23145 * adjacent.
23146 */
23148
23149 /*
23150 * Phase 3: single linear pass verifying that adjacent entries sharing a
23151 * parent index have identical extension dependencies, and keeping one
23152 * representative entry per parent index.
23153 */
23155 {
23156 if (prev != NULL && prev->parentIndexOid == entry->parentIndexOid)
23157 {
23158 if (!equal_oid_lists(prev->extensionOids, entry->extensionOids))
23159 ereport(ERROR,
23161 errmsg("cannot merge partitions with conflicting extension dependencies"),
23162 errdetail("Partition indexes \"%s\" and \"%s\" depend on different extensions.",
23163 get_rel_name(prev->indexOid),
23164 get_rel_name(entry->indexOid))));
23165
23166 /* Duplicate entry for the same parent index; discard. */
23167 list_free(entry->extensionOids);
23168 pfree(entry);
23169 continue;
23170 }
23171
23172 result = lappend(result, entry);
23173 prev = entry;
23174 }
23175
23177
23178 return result;
23179}
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:1387
List * getAutoExtensionsOfObject(Oid classId, Oid objectId)
Definition pg_depend.c:780
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 20054 of file tablecmds.c.

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

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

17741{
17744
17745 if (acon->condeferrable != bcon->condeferrable ||
17746 acon->condeferred != bcon->condeferred ||
17749 return false;
17750 else
17751 return true;
17752}
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 14066 of file tablecmds.c.

14069{
14072
14073 /*
14074 * Note: for a self-referential FK (referencing and referenced tables are
14075 * the same), it is important that the ON UPDATE action fires before the
14076 * CHECK action, since both triggers will fire on the same row during an
14077 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
14078 * state of the row. Triggers fire in name order, so we ensure this by
14079 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
14080 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
14081 */
14083 fk_trigger->replace = false;
14084 fk_trigger->isconstraint = true;
14085 fk_trigger->trigname = "RI_ConstraintTrigger_c";
14086 fk_trigger->relation = NULL;
14087
14088 /* Either ON INSERT or ON UPDATE */
14089 if (on_insert)
14090 {
14091 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
14093 }
14094 else
14095 {
14096 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
14098 }
14099
14100 fk_trigger->args = NIL;
14101 fk_trigger->row = true;
14103 fk_trigger->columns = NIL;
14104 fk_trigger->whenClause = NULL;
14105 fk_trigger->transitionRels = NIL;
14106 fk_trigger->deferrable = fkconstraint->deferrable;
14107 fk_trigger->initdeferred = fkconstraint->initdeferred;
14108 fk_trigger->constrrel = NULL;
14109
14111 constraintOid, indexOid, InvalidOid,
14112 parentTrigOid, NULL, true, false);
14113
14114 /* Make changes-so-far visible */
14116
14117 return trigAddress.objectId;
14118}
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 14129 of file tablecmds.c.

14133{
14136
14137 /*
14138 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14139 * DELETE action on the referenced table.
14140 */
14142 fk_trigger->replace = false;
14143 fk_trigger->isconstraint = true;
14144 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14145 fk_trigger->relation = NULL;
14146 fk_trigger->args = NIL;
14147 fk_trigger->row = true;
14150 fk_trigger->columns = NIL;
14151 fk_trigger->whenClause = NULL;
14152 fk_trigger->transitionRels = NIL;
14153 fk_trigger->constrrel = NULL;
14154
14155 switch (fkconstraint->fk_del_action)
14156 {
14158 fk_trigger->deferrable = fkconstraint->deferrable;
14159 fk_trigger->initdeferred = fkconstraint->initdeferred;
14160 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
14161 break;
14163 fk_trigger->deferrable = false;
14164 fk_trigger->initdeferred = false;
14165 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
14166 break;
14168 fk_trigger->deferrable = false;
14169 fk_trigger->initdeferred = false;
14170 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
14171 break;
14173 fk_trigger->deferrable = false;
14174 fk_trigger->initdeferred = false;
14175 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
14176 break;
14178 fk_trigger->deferrable = false;
14179 fk_trigger->initdeferred = false;
14180 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
14181 break;
14182 default:
14183 elog(ERROR, "unrecognized FK action type: %d",
14184 (int) fkconstraint->fk_del_action);
14185 break;
14186 }
14187
14189 constraintOid, indexOid, InvalidOid,
14190 parentDelTrigger, NULL, true, false);
14191 if (deleteTrigOid)
14192 *deleteTrigOid = trigAddress.objectId;
14193
14194 /* Make changes-so-far visible */
14196
14197 /*
14198 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14199 * UPDATE action on the referenced table.
14200 */
14202 fk_trigger->replace = false;
14203 fk_trigger->isconstraint = true;
14204 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14205 fk_trigger->relation = NULL;
14206 fk_trigger->args = NIL;
14207 fk_trigger->row = true;
14210 fk_trigger->columns = NIL;
14211 fk_trigger->whenClause = NULL;
14212 fk_trigger->transitionRels = NIL;
14213 fk_trigger->constrrel = NULL;
14214
14215 switch (fkconstraint->fk_upd_action)
14216 {
14218 fk_trigger->deferrable = fkconstraint->deferrable;
14219 fk_trigger->initdeferred = fkconstraint->initdeferred;
14220 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
14221 break;
14223 fk_trigger->deferrable = false;
14224 fk_trigger->initdeferred = false;
14225 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
14226 break;
14228 fk_trigger->deferrable = false;
14229 fk_trigger->initdeferred = false;
14230 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
14231 break;
14233 fk_trigger->deferrable = false;
14234 fk_trigger->initdeferred = false;
14235 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
14236 break;
14238 fk_trigger->deferrable = false;
14239 fk_trigger->initdeferred = false;
14240 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
14241 break;
14242 default:
14243 elog(ERROR, "unrecognized FK action type: %d",
14244 (int) fkconstraint->fk_upd_action);
14245 break;
14246 }
14247
14249 constraintOid, indexOid, InvalidOid,
14250 parentUpdTrigger, NULL, true, false);
14251 if (updateTrigOid)
14252 *updateTrigOid = trigAddress.objectId;
14253}
#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 14264 of file tablecmds.c.

14269{
14271 constraintOid, indexOid,
14272 parentInsTrigger, true);
14274 constraintOid, indexOid,
14275 parentUpdTrigger, false);
14276}
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 17643 of file tablecmds.c.

17644{
17646 SysScanDesc scan;
17650
17651 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17653
17654 /*
17655 * Check for duplicates in the list of parents, and determine the highest
17656 * inhseqno already present; we'll use the next one for the new parent.
17657 * Also, if proposed child is a partition, it cannot already be
17658 * inheriting.
17659 *
17660 * Note: we do not reject the case where the child already inherits from
17661 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17662 */
17663 ScanKeyInit(&key,
17668 true, NULL, 1, &key);
17669
17670 /* inhseqno sequences start at 1 */
17671 inhseqno = 0;
17673 {
17675
17676 if (inh->inhparent == RelationGetRelid(parent_rel))
17677 ereport(ERROR,
17679 errmsg("relation \"%s\" would be inherited from more than once",
17681
17682 if (inh->inhseqno > inhseqno)
17683 inhseqno = inh->inhseqno;
17684 }
17685 systable_endscan(scan);
17686
17687 /* Match up the columns and bump attinhcount as needed */
17689
17690 /* Match up the constraints and bump coninhcount as needed */
17692
17693 /*
17694 * OK, it looks valid. Make the catalog entries that show inheritance.
17695 */
17698 inhseqno + 1,
17700 parent_rel->rd_rel->relkind ==
17702
17703 /* Now we're done with pg_inherits */
17705}
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 22755 of file tablecmds.c.

22757{
22759 Oid newRelId;
22762 List *colList = NIL;
22763 Oid relamId;
22767
22768 /* If the existing rel is temp, it must belong to this session. */
22770 ereport(ERROR,
22772 errmsg("cannot create as partition of temporary relation of another session"));
22773
22774 /* Look up inheritance ancestors and generate the relation schema. */
22776
22777 /* Create a tuple descriptor from the relation schema. */
22779
22780 /* Look up the access method for the new relation. */
22782
22783 /* Look up the namespace in which we are supposed to create the relation. */
22784 namespaceId =
22787 ereport(ERROR,
22789 errmsg("relation \"%s\" already exists", newPartName->relname));
22790
22791 /*
22792 * We intended to create the partition with the same persistence as the
22793 * parent table, but we still need to recheck because that might be
22794 * affected by the search_path. If the parent is permanent, so must be
22795 * all of its partitions.
22796 */
22797 if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22798 newPartName->relpersistence == RELPERSISTENCE_TEMP)
22799 ereport(ERROR,
22801 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
22803
22804 /* Permanent rels cannot be partitions belonging to a temporary parent. */
22805 if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
22806 parent_relform->relpersistence == RELPERSISTENCE_TEMP)
22807 ereport(ERROR,
22809 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
22811
22812 /* Create the relation. */
22815 parent_relform->reltablespace,
22816 InvalidOid,
22817 InvalidOid,
22818 InvalidOid,
22819 ownerId,
22820 relamId,
22821 descriptor,
22822 NIL,
22824 newPartName->relpersistence,
22825 false,
22826 false,
22828 (Datum) 0,
22829 true,
22831 true,
22832 InvalidOid,
22833 NULL);
22834
22835 /*
22836 * We must bump the command counter to make the newly-created relation
22837 * tuple visible for opening.
22838 */
22840
22841 /*
22842 * Open the new partition with no lock, because we already have an
22843 * AccessExclusiveLock placed there after creation.
22844 */
22846
22847 /* Find or create a work queue entry for the newly created table. */
22849
22850 /* Create constraints, default values, and generated values. */
22852
22853 /*
22854 * Need to call CommandCounterIncrement, so a fresh relcache entry has
22855 * newly installed constraint info.
22856 */
22858
22859 return newRel;
22860}
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:1136
@ 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 23479 of file tablecmds.c.

23480{
23482
23484 pc->partRel = partRel;
23485
23486 /*
23487 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
23488 * don't bother using it.
23489 */
23490 pc->bistate = GetBulkInsertState();
23491
23492 /* Create a destination tuple slot for the new partition. */
23493 pc->dstslot = table_slot_create(pc->partRel, NULL);
23494
23495 return pc;
23496}
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 22548 of file tablecmds.c.

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

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

17713{
17715 bool isnull;
17716 Datum attr;
17717 Datum expr;
17718
17720 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17721 if (isnull)
17722 elog(ERROR, "null conbin for constraint %u", con->oid);
17723
17724 expr = DirectFunctionCall2(pg_get_expr, attr,
17725 ObjectIdGetDatum(con->conrelid));
17726 return TextDatumGetCString(expr);
17727}
#define DirectFunctionCall2(func, arg1, arg2)
Definition fmgr.h:686
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:3912
List * AddRelationNotNullConstraints(Relation rel, List *constraints, List *old_notnulls, List *existing_constraints)
Definition heap.c:2911
Oid get_rel_relam(Oid relid)
Definition lsyscache.c:2293
Oid get_rel_tablespace(Oid relid)
Definition lsyscache.c:2247
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 23502 of file tablecmds.c.

23503{
23504 ListCell *ltab;
23505
23507 FreeBulkInsertState(pc->bistate);
23508
23509 table_finish_bulk_insert(pc->partRel, ti_options);
23510
23511 /*
23512 * We don't need to process this pc->partRel so delete the ALTER TABLE
23513 * queue of it.
23514 */
23515 foreach(ltab, *wqueue)
23516 {
23518
23519 if (tab->relid == RelationGetRelid(pc->partRel))
23520 {
23522 break;
23523 }
23524 }
23525
23526 pfree(pc);
23527}
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 21416 of file tablecmds.c.

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

23024{
23025 /* Remove the pg_inherits row first. */
23027
23028 /*
23029 * Detaching the partition might involve TOAST table access, so ensure we
23030 * have a valid snapshot.
23031 */
23033
23034 /* Do the final part of detaching. */
23036
23038}

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

18430{
18432 SysScanDesc scan;
18433 ScanKeyData key[3];
18435
18437
18438 ScanKeyInit(&key[0],
18442 ScanKeyInit(&key[1],
18445 ObjectIdGetDatum(relid));
18446 ScanKeyInit(&key[2],
18449 Int32GetDatum(0));
18450
18452 NULL, 3, key);
18453
18455 {
18457
18458 if (dep->refclassid == refclassid &&
18459 dep->refobjid == refobjid &&
18460 dep->refobjsubid == 0 &&
18461 dep->deptype == deptype)
18463 }
18464
18465 systable_endscan(scan);
18467}

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

21785{
21787 SysScanDesc scan;
21790 ObjectAddresses *objects;
21791
21792 objects = new_object_addresses();
21793
21794 /*
21795 * Scan pg_trigger to search for all triggers on this rel.
21796 */
21801 true, NULL, 1, &skey);
21803 {
21806
21807 /* Ignore triggers that weren't cloned */
21808 if (!OidIsValid(pg_trigger->tgparentid))
21809 continue;
21810
21811 /*
21812 * Ignore internal triggers that are implementation objects of foreign
21813 * keys, because these will be detached when the foreign keys
21814 * themselves are.
21815 */
21816 if (OidIsValid(pg_trigger->tgconstrrelid))
21817 continue;
21818
21819 /*
21820 * This is ugly, but necessary: remove the dependency markings on the
21821 * trigger so that it can be removed.
21822 */
21829
21830 /* remember this trigger to remove it below */
21832 add_exact_object_address(&trig, objects);
21833 }
21834
21835 /* make the dependency removal visible to the deletion below */
21838
21839 /* done */
21840 free_object_addresses(objects);
21841 systable_endscan(scan);
21843}

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

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

12108{
12110 SysScanDesc scan;
12112
12113 ScanKeyInit(&key,
12116 ObjectIdGetDatum(conoid));
12118 NULL, 1, &key);
12119 while ((trigtup = systable_getnext(scan)) != NULL)
12120 {
12123
12124 /* Invalid if trigger is not for a referential integrity constraint */
12125 if (!OidIsValid(trgform->tgconstrrelid))
12126 continue;
12127 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12128 continue;
12129 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12130 continue;
12131
12132 /* We should be dropping trigger related to foreign key constraint */
12133 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12134 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12135 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12136 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12137 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12138 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12139 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12140 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12141 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12142 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12143 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12144 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12145
12146 /*
12147 * The constraint is originally set up to contain this trigger as an
12148 * implementation object, so there's a dependency record that links
12149 * the two; however, since the trigger is no longer needed, we remove
12150 * the dependency link in order to be able to drop the trigger while
12151 * keeping the constraint intact.
12152 */
12154 trgform->oid,
12155 false);
12156 /* make dependency deletion visible to performDeletion */
12159 trgform->oid);
12161 /* make trigger drop visible, in case the loop iterates */
12163 }
12164
12165 systable_endscan(scan);
12166}

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

23046{
23047 ListCell *la,
23048 *lb;
23049
23050 if (list_length(a) != list_length(b))
23051 return false;
23052
23053 forboth(la, a, lb, b)
23054 {
23055 if (lfirst_oid(la) != lfirst_oid(lb))
23056 return false;
23057 }
23058 return true;
23059}

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

22454{
22455 econtext->ecxt_scantuple = insertslot;
22456
22458 {
22459 if (!ex->is_generated)
22460 continue;
22461
22462 insertslot->tts_values[ex->attnum - 1]
22463 = ExecEvalExpr(ex->exprstate,
22464 econtext,
22465 &insertslot->tts_isnull[ex->attnum - 1]);
22466 }
22467
22469 {
22470 switch (con->contype)
22471 {
22472 case CONSTR_CHECK:
22473 if (!ExecCheck(con->qualstate, econtext))
22474 ereport(ERROR,
22476 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
22478 errtableconstraint(newPartRel, con->name));
22479 break;
22480 case CONSTR_NOTNULL:
22481 case CONSTR_FOREIGN:
22482 /* Nothing to do here */
22483 break;
22484 default:
22485 elog(ERROR, "unrecognized constraint type: %d",
22486 (int) con->contype);
22487 }
22488 }
22489}
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:712
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:3785
void heap_truncate_check_FKs(List *relations, bool tempTables)
Definition heap.c:3690
void heap_truncate_one_rel(Relation rel)
Definition heap.c:3646
#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:3765
List * es_opened_result_relations
Definition execnodes.h:724
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:513
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 13908 of file tablecmds.c.

13909{
13910 CoercionPathType ret;
13911
13913 {
13915 *funcid = InvalidOid;
13916 }
13917 else
13918 {
13920 COERCION_IMPLICIT, funcid);
13921 if (ret == COERCION_PATH_NONE)
13922 /* A previously-relied-upon cast is now gone. */
13923 elog(ERROR, "could not find cast from %u to %u",
13925 }
13926
13927 return ret;
13928}
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 23248 of file tablecmds.c.

23249{
23251 {
23252 list_free(entry->extensionOids);
23253 pfree(entry);
23254 }
23256}

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

22335{
22336 char cmethod;
22337
22338 if (compression == NULL || strcmp(compression, "default") == 0)
22340
22341 /*
22342 * To specify a nondefault method, the column data type must be toastable.
22343 * Note this says nothing about whether the column's attstorage setting
22344 * permits compression; we intentionally allow attstorage and
22345 * attcompression to be independent. But with a non-toastable type,
22346 * attstorage could not be set to a value that would permit compression.
22347 *
22348 * We don't actually need to enforce this, since nothing bad would happen
22349 * if attcompression were non-default; it would never be consulted. But
22350 * it seems more user-friendly to complain about a certainly-useless
22351 * attempt to set the property.
22352 */
22354 ereport(ERROR,
22356 errmsg("column data type %s does not support compression",
22358
22359 cmethod = CompressionNameToMethod(compression);
22361 ereport(ERROR,
22363 errmsg("invalid compression method \"%s\"", compression)));
22364
22365 return cmethod;
22366}
#define TypeIsToastable(typid)
Definition lsyscache.h:223
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 22495 of file tablecmds.c.

22496{
22499 List *colList = NIL;
22500
22502
22503 for (parent_attno = 1; parent_attno <= modelDesc->natts;
22504 parent_attno++)
22505 {
22507 parent_attno - 1);
22508 ColumnDef *def;
22509
22510 /* Ignore dropped columns in the parent. */
22511 if (attribute->attisdropped)
22512 continue;
22513
22514 def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
22515 attribute->atttypmod, attribute->attcollation);
22516
22517 def->is_not_null = attribute->attnotnull;
22518
22519 /* Copy identity. */
22520 def->identity = attribute->attidentity;
22521
22522 /* Copy attgenerated. */
22523 def->generated = attribute->attgenerated;
22524
22525 def->storage = attribute->attstorage;
22526
22527 /* Likewise, copy compression. */
22528 if (CompressionMethodIsValid(attribute->attcompression))
22529 def->compression =
22530 pstrdup(GetCompressionMethodName(attribute->attcompression));
22531 else
22532 def->compression = NULL;
22533
22534 /* Add to column list. */
22535 colList = lappend(colList, def);
22536 }
22537
22538 return colList;
22539}
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 22372 of file tablecmds.c.

22373{
22374 char cstorage = 0;
22375
22376 if (pg_strcasecmp(storagemode, "plain") == 0)
22378 else if (pg_strcasecmp(storagemode, "external") == 0)
22380 else if (pg_strcasecmp(storagemode, "extended") == 0)
22382 else if (pg_strcasecmp(storagemode, "main") == 0)
22384 else if (pg_strcasecmp(storagemode, "default") == 0)
22386 else
22387 ereport(ERROR,
22389 errmsg("invalid storage type \"%s\"",
22390 storagemode)));
22391
22392 /*
22393 * safety check: do not allow toasted storage modes unless column datatype
22394 * is TOAST-aware.
22395 */
22397 ereport(ERROR,
22399 errmsg("column data type %s can only have storage PLAIN",
22401
22402 return cstorage;
22403}
char get_typstorage(Oid typid)
Definition lsyscache.c:2614
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 12174 of file tablecmds.c.

12178{
12180 SysScanDesc scan;
12182
12184 ScanKeyInit(&key,
12187 ObjectIdGetDatum(conoid));
12188
12190 NULL, 1, &key);
12191 while ((trigtup = systable_getnext(scan)) != NULL)
12192 {
12194
12195 if (trgform->tgconstrrelid != conrelid)
12196 continue;
12197 if (trgform->tgrelid != confrelid)
12198 continue;
12199 /* Only ever look at "action" triggers on the PK side. */
12201 continue;
12202 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12203 {
12205 *deleteTriggerOid = trgform->oid;
12206 }
12207 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12208 {
12210 *updateTriggerOid = trgform->oid;
12211 }
12212#ifndef USE_ASSERT_CHECKING
12213 /* In an assert-enabled build, continue looking to find duplicates */
12215 break;
12216#endif
12217 }
12218
12220 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12221 conoid);
12223 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12224 conoid);
12225
12226 systable_endscan(scan);
12227}
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 12235 of file tablecmds.c.

12239{
12241 SysScanDesc scan;
12243
12245 ScanKeyInit(&key,
12248 ObjectIdGetDatum(conoid));
12249
12251 NULL, 1, &key);
12252 while ((trigtup = systable_getnext(scan)) != NULL)
12253 {
12255
12256 if (trgform->tgconstrrelid != confrelid)
12257 continue;
12258 if (trgform->tgrelid != conrelid)
12259 continue;
12260 /* Only ever look at "check" triggers on the FK side. */
12262 continue;
12263 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12264 {
12266 *insertTriggerOid = trgform->oid;
12267 }
12268 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12269 {
12271 *updateTriggerOid = trgform->oid;
12272 }
12273#ifndef USE_ASSERT_CHECKING
12274 /* In an assert-enabled build, continue looking to find duplicates. */
12276 break;
12277#endif
12278 }
12279
12281 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12282 conoid);
12284 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12285 conoid);
12286
12287 systable_endscan(scan);
12288}
#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 22232 of file tablecmds.c.

22233{
22235 HeapTuple tuple;
22236 SysScanDesc scan;
22237 ScanKeyData key[2];
22238 List *constraints = NIL;
22239
22240 /*
22241 * If no indexes, or no columns are referenceable by FKs, we can avoid the
22242 * scan.
22243 */
22247 return NIL;
22248
22249 /* Search for constraints referencing this table */
22251 ScanKeyInit(&key[0],
22254 ScanKeyInit(&key[1],
22257
22258 /* XXX This is a seqscan, as we don't have a usable index */
22259 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
22260 while ((tuple = systable_getnext(scan)) != NULL)
22261 {
22263
22264 /*
22265 * We only need to process constraints that are part of larger ones.
22266 */
22267 if (!OidIsValid(constrForm->conparentid))
22268 continue;
22269
22270 constraints = lappend_oid(constraints, constrForm->oid);
22271 }
22272
22273 systable_endscan(scan);
22275
22276 return constraints;
22277}
#define bms_is_empty(a)
Definition bitmapset.h:118
@ INDEX_ATTR_BITMAP_KEY
Definition relcache.h:69

References AccessShareLock, bms_is_empty, BTEqualStrategyNumber, CharGetDatum(), fb(), Form_pg_constraint, GETSTRUCT(), INDEX_ATTR_BITMAP_KEY, InvalidOid, lappend_oid(), NIL, ObjectIdGetDatum(), OidIsValid, RelationGetIndexAttrBitmap(), RelationGetIndexList(), RelationGetRelid, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATDetachCheckNoForeignKeyRefs(), and DetachPartitionFinalize().

◆ index_copy_data()

static void index_copy_data ( Relation  rel,
RelFileLocator  newrlocator 
)
static

Definition at line 17420 of file tablecmds.c.

17421{
17423
17424 /*
17425 * Since we copy the file directly without looking at the shared buffers,
17426 * we'd better first flush out any pages of the source relation that are
17427 * in shared buffers. We assume no new changes will be made while we are
17428 * holding exclusive lock on the rel.
17429 */
17431
17432 /*
17433 * Create and copy all forks of the relation, and schedule unlinking of
17434 * old physical files.
17435 *
17436 * NOTE: any conflict in relfilenumber value will be caught in
17437 * RelationCreateStorage().
17438 */
17439 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17440
17441 /* copy main fork */
17443 rel->rd_rel->relpersistence);
17444
17445 /* copy those extra forks that exist */
17446 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17447 forkNum <= MAX_FORKNUM; forkNum++)
17448 {
17449 if (smgrexists(RelationGetSmgr(rel), forkNum))
17450 {
17451 smgrcreate(dstrel, forkNum, false);
17452
17453 /*
17454 * WAL log creation if the relation is persistent, or this is the
17455 * init fork of an unlogged relation.
17456 */
17457 if (RelationIsPermanent(rel) ||
17458 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17459 forkNum == INIT_FORKNUM))
17460 log_smgrcreate(&newrlocator, forkNum);
17462 rel->rd_rel->relpersistence);
17463 }
17464 }
17465
17466 /* drop old relation, and close new one */
17469}
void FlushRelationBuffers(Relation rel)
Definition bufmgr.c:5162
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 18131 of file tablecmds.c.

18132{
18134 SysScanDesc scan;
18137 bool found = false;
18138
18139 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
18140
18141 /*
18142 * Find pg_inherits entries by inhparent. (We need to scan them all in
18143 * order to verify that no other partition is pending detach.)
18144 */
18146 ScanKeyInit(&key,
18151 true, NULL, 1, &key);
18152
18154 {
18156
18158 if (inhForm->inhdetachpending)
18159 ereport(ERROR,
18161 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
18162 get_rel_name(inhForm->inhrelid),
18163 get_namespace_name(parent_rel->rd_rel->relnamespace),
18165 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
18166
18167 if (inhForm->inhrelid == RelationGetRelid(child_rel))
18168 {
18170
18173
18175 &inheritsTuple->t_self,
18176 newtup);
18177 found = true;
18179 /* keep looking, to ensure we catch others pending detach */
18180 }
18181 }
18182
18183 /* Done */
18184 systable_endscan(scan);
18186
18187 if (!found)
18188 ereport(ERROR,
18190 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18193}
#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 17769 of file tablecmds.c.

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

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

17908{
17913 Oid parent_relid = RelationGetRelid(parent_rel);
17914 AttrMap *attmap;
17915
17917
17918 /* Outer loop scans through the parent's constraint definitions */
17922 ObjectIdGetDatum(parent_relid));
17924 true, NULL, 1, &parent_key);
17925
17928 true);
17929
17931 {
17937 bool found = false;
17938
17939 if (parent_con->contype != CONSTRAINT_CHECK &&
17940 parent_con->contype != CONSTRAINT_NOTNULL)
17941 continue;
17942
17943 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17944 if (parent_con->connoinherit)
17945 continue;
17946
17947 if (parent_con->contype == CONSTRAINT_NOTNULL)
17949 else
17951
17952 /* Search for a child constraint matching this one */
17958 true, NULL, 1, &child_key);
17959
17961 {
17964
17965 if (child_con->contype != parent_con->contype)
17966 continue;
17967
17968 /*
17969 * CHECK constraint are matched by constraint name, NOT NULL ones
17970 * by attribute number.
17971 */
17972 if (child_con->contype == CONSTRAINT_CHECK)
17973 {
17974 if (strcmp(NameStr(parent_con->conname),
17975 NameStr(child_con->conname)) != 0)
17976 continue;
17977 }
17978 else if (child_con->contype == CONSTRAINT_NOTNULL)
17979 {
17983
17986 if (parent_attno != attmap->attnums[child_attno - 1])
17987 continue;
17988
17990 /* there shouldn't be constraints on dropped columns */
17991 if (parent_attr->attisdropped || child_attr->attisdropped)
17992 elog(ERROR, "found not-null constraint on dropped columns");
17993 }
17994
17995 if (child_con->contype == CONSTRAINT_CHECK &&
17997 ereport(ERROR,
17999 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
18001
18002 /*
18003 * If the child constraint is "no inherit" then cannot merge
18004 */
18005 if (child_con->connoinherit)
18006 ereport(ERROR,
18008 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
18010
18011 /*
18012 * If the child constraint is "not valid" then cannot merge with a
18013 * valid parent constraint
18014 */
18015 if (parent_con->convalidated && child_con->conenforced &&
18016 !child_con->convalidated)
18017 ereport(ERROR,
18019 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
18021
18022 /*
18023 * A NOT ENFORCED child constraint cannot be merged with an
18024 * ENFORCED parent constraint. However, the reverse is allowed,
18025 * where the child constraint is ENFORCED.
18026 */
18027 if (parent_con->conenforced && !child_con->conenforced)
18028 ereport(ERROR,
18030 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
18032
18033 /*
18034 * OK, bump the child constraint's inheritance count. (If we fail
18035 * later on, this change will just roll back.)
18036 */
18039
18040 if (pg_add_s16_overflow(child_con->coninhcount, 1,
18041 &child_con->coninhcount))
18042 ereport(ERROR,
18044 errmsg("too many inheritance parents"));
18045
18046 /*
18047 * In case of partitions, an inherited constraint must be
18048 * inherited only once since it cannot have multiple parents and
18049 * it is never considered local.
18050 */
18051 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18052 {
18053 Assert(child_con->coninhcount == 1);
18054 child_con->conislocal = false;
18055 }
18056
18059
18060 found = true;
18061 break;
18062 }
18063
18065
18066 if (!found)
18067 {
18068 if (parent_con->contype == CONSTRAINT_NOTNULL)
18069 ereport(ERROR,
18071 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
18072 get_attname(parent_relid,
18074 false),
18076
18077 ereport(ERROR,
18079 errmsg("child table is missing constraint \"%s\"",
18080 NameStr(parent_con->conname))));
18081 }
18082 }
18083
18086}
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 22868 of file tablecmds.c.

22869{
22870 CommandId mycid;
22871 EState *estate;
22872 AlteredTableInfo *tab;
22873 ListCell *ltab;
22874
22875 /* The FSM is empty, so don't bother using it. */
22876 uint32 ti_options = TABLE_INSERT_SKIP_FSM;
22877 BulkInsertState bistate; /* state of bulk inserts for partition */
22878 TupleTableSlot *dstslot;
22879
22880 /* Find the work queue entry for the new partition table: newPartRel. */
22882
22883 /* Generate the constraint and default execution states. */
22884 estate = CreateExecutorState();
22885
22887
22888 mycid = GetCurrentCommandId(true);
22889
22890 /* Prepare a BulkInsertState for table_tuple_insert. */
22891 bistate = GetBulkInsertState();
22892
22893 /* Create the necessary tuple slot. */
22894 dstslot = table_slot_create(newPartRel, NULL);
22895
22897 {
22898 ExprContext *econtext;
22901 TableScanDesc scan;
22903 Snapshot snapshot;
22905
22906 econtext = GetPerTupleExprContext(estate);
22907
22908 /*
22909 * Partition is already locked in the transformPartitionCmdForMerge
22910 * function.
22911 */
22913
22914 /* Create a source tuple slot for the partition being merged. */
22916
22917 /*
22918 * Map computing for moving attributes of the merged partition to the
22919 * new partition.
22920 */
22923
22924 /* Scan through the rows. */
22925 snapshot = RegisterSnapshot(GetLatestSnapshot());
22926 scan = table_beginscan(mergingPartition, snapshot, 0, NULL,
22927 SO_NONE);
22928
22929 /*
22930 * Switch to per-tuple memory context and reset it for each tuple
22931 * produced, so we don't leak memory.
22932 */
22934
22936 {
22938
22940
22941 if (tuple_map)
22942 {
22943 /* Need to use a map to copy attributes. */
22944 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
22945 }
22946 else
22947 {
22949
22950 /* Copy attributes directly. */
22951 insertslot = dstslot;
22952
22954
22955 memcpy(insertslot->tts_values, srcslot->tts_values,
22956 sizeof(Datum) * srcslot->tts_nvalid);
22957 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
22958 sizeof(bool) * srcslot->tts_nvalid);
22959
22961 }
22962
22963 /*
22964 * Constraints and GENERATED expressions might reference the
22965 * tableoid column, so fill tts_tableOid with the desired value.
22966 * (We must do this each time, because it gets overwritten with
22967 * newrel's OID during storing.)
22968 */
22969 insertslot->tts_tableOid = RelationGetRelid(newPartRel);
22970
22971 /*
22972 * Now, evaluate any generated expressions whose inputs come from
22973 * the new tuple. We assume these columns won't reference each
22974 * other, so that there's no ordering dependency.
22975 */
22977 insertslot, econtext);
22978
22979 /* Write the tuple out to the new relation. */
22981 ti_options, bistate);
22982
22983 ResetExprContext(econtext);
22984 }
22985
22987 table_endscan(scan);
22988 UnregisterSnapshot(snapshot);
22989
22990 if (tuple_map)
22992
22995 }
22996
22997 FreeExecutorState(estate);
22999 FreeBulkInsertState(bistate);
23000
23002
23003 /*
23004 * We don't need to process this newPartRel since we already processed it
23005 * here, so delete the ALTER TABLE queue for it.
23006 */
23007 foreach(ltab, *wqueue)
23008 {
23009 tab = (AlteredTableInfo *) lfirst(ltab);
23010 if (tab->relid == RelationGetRelid(newPartRel))
23011 {
23013 break;
23014 }
23015 }
23016}
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 8204 of file tablecmds.c.

8205{
8207
8208 nnulltest->arg = (Expr *) makeVar(1,
8209 attr->attnum,
8210 attr->atttypid,
8211 attr->atttypmod,
8212 attr->attcollation,
8213 0);
8214 nnulltest->nulltesttype = IS_NOT_NULL;
8215
8216 /*
8217 * argisrow = false is correct even for a composite column, because
8218 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8219 * case, just IS DISTINCT FROM NULL.
8220 */
8221 nnulltest->argisrow = false;
8222 nnulltest->location = -1;
8223
8225 {
8227 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8228 RelationGetRelationName(rel), NameStr(attr->attname))));
8229 return true;
8230 }
8231
8232 return false;
8233}
@ 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 20328 of file tablecmds.c.

20330{
20332 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20333 int i;
20334
20335 if (constr && constr->has_not_null)
20336 {
20337 int natts = scanrel->rd_att->natts;
20338
20339 for (i = 1; i <= natts; i++)
20340 {
20341 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20342
20343 /* invalid not-null constraint must be ignored here */
20344 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20345 {
20348
20349 ntest->arg = (Expr *) makeVar(1,
20350 i,
20351 wholeatt->atttypid,
20352 wholeatt->atttypmod,
20353 wholeatt->attcollation,
20354 0);
20355 ntest->nulltesttype = IS_NOT_NULL;
20356
20357 /*
20358 * argisrow=false is correct even for a composite column,
20359 * because attnotnull does not represent a SQL-spec IS NOT
20360 * NULL test in such a case, just IS DISTINCT FROM NULL.
20361 */
20362 ntest->argisrow = false;
20363 ntest->location = -1;
20365 }
20366 }
20367 }
20368
20370}

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

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

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

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

13272{
13274 AlteredTableInfo *tab;
13277
13279 Assert(con->contype == CONSTRAINT_FOREIGN);
13280 Assert(!con->convalidated);
13281
13282 /*
13283 * Add the validation to phase 3's queue; not needed for partitioned
13284 * tables themselves, only for their partitions.
13285 *
13286 * When the referenced table (pkrelid) is partitioned, the referencing
13287 * table (fkrel) has one pg_constraint row pointing to each partition
13288 * thereof. These rows are there only to support action triggers and no
13289 * table scan is needed, therefore skip this for them as well.
13290 */
13291 if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13292 con->confrelid == pkrelid)
13293 {
13296
13297 /* Queue validation for phase 3 */
13299 /* for now this is all we need */
13300 fkconstraint->conname = pstrdup(NameStr(con->conname));
13301
13303 newcon->name = fkconstraint->conname;
13304 newcon->contype = CONSTR_FOREIGN;
13305 newcon->refrelid = con->confrelid;
13306 newcon->refindid = con->conindid;
13307 newcon->conid = con->oid;
13308 newcon->qual = (Node *) fkconstraint;
13309
13310 /* Find or create work queue entry for this table */
13312 tab->constraints = lappend(tab->constraints, newcon);
13313 }
13314
13315 /*
13316 * If the table at either end of the constraint is partitioned, we need to
13317 * recurse and handle every unvalidated constraint that is a child of this
13318 * constraint.
13319 */
13320 if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13321 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13322 {
13323 ScanKeyData pkey;
13326
13327 ScanKeyInit(&pkey,
13330 ObjectIdGetDatum(con->oid));
13331
13333 true, NULL, 1, &pkey);
13334
13336 {
13339
13341
13342 /*
13343 * If the child constraint has already been validated, no further
13344 * action is required for it or its descendants, as they are all
13345 * valid.
13346 */
13347 if (childcon->convalidated)
13348 continue;
13349
13350 childrel = table_open(childcon->conrelid, lockmode);
13351
13352 /*
13353 * NB: Note that pkrelid should be passed as-is during recursion,
13354 * as it is required to identify the root referenced table.
13355 */
13357 childtup, lockmode);
13359 }
13360
13362 }
13363
13364 /*
13365 * Now mark the pg_constraint row as validated (even if we didn't check,
13366 * notably the ones for partitions on the referenced side).
13367 *
13368 * We rely on transaction abort to roll back this change if phase 3
13369 * ultimately finds violating rows. This is a bit ugly.
13370 */
13373 copy_con->convalidated = true;
13375
13377
13379}

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

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

◆ QueueNNConstraintValidation()

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

Definition at line 13492 of file tablecmds.c.

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

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

20449{
20450 /*
20451 * Based on the table's existing constraints, determine whether or not we
20452 * may skip scanning the table.
20453 */
20455 {
20456 if (!validate_default)
20458 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20459 RelationGetRelationName(scanrel))));
20460 else
20462 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20463 RelationGetRelationName(scanrel))));
20464 return;
20465 }
20466
20467 /*
20468 * Constraints proved insufficient. For plain relations, queue a
20469 * validation item now; for partitioned tables, recurse to process each
20470 * partition.
20471 */
20472 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20473 {
20474 AlteredTableInfo *tab;
20475
20476 /* Grab a work queue entry. */
20477 tab = ATGetQueueEntry(wqueue, scanrel);
20480 tab->validate_default = validate_default;
20481 }
20482 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20483 {
20484 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20485 int i;
20486
20487 for (i = 0; i < partdesc->nparts; i++)
20488 {
20491
20492 /*
20493 * This is the minimum lock we need to prevent deadlocks.
20494 */
20496
20497 /*
20498 * Adjust the constraint for scanrel so that it matches this
20499 * partition's attribute numbers.
20500 */
20503 part_rel, scanrel);
20504
20507 validate_default);
20508 table_close(part_rel, NoLock); /* keep lock till commit */
20509 }
20510 }
20511}
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 19850 of file tablecmds.c.

19852{
19853 Node *stmt = (Node *) arg;
19854 ObjectType reltype;
19855 HeapTuple tuple;
19858 char relkind;
19859
19860 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
19861 if (!HeapTupleIsValid(tuple))
19862 return; /* concurrently dropped */
19864 relkind = classform->relkind;
19865
19866 /* Must own relation. */
19869
19870 /* No system table modifications unless explicitly allowed. */
19872 ereport(ERROR,
19874 errmsg("permission denied: \"%s\" is a system catalog",
19875 rv->relname)));
19876
19877 /*
19878 * Extract the specified relation type from the statement parse tree.
19879 *
19880 * Also, for ALTER .. RENAME, check permissions: the user must (still)
19881 * have CREATE rights on the containing namespace.
19882 */
19883 if (IsA(stmt, RenameStmt))
19884 {
19887 if (aclresult != ACLCHECK_OK)
19889 get_namespace_name(classform->relnamespace));
19890 reltype = ((RenameStmt *) stmt)->renameType;
19891 }
19892 else if (IsA(stmt, AlterObjectSchemaStmt))
19893 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
19894
19895 else if (IsA(stmt, AlterTableStmt))
19896 reltype = ((AlterTableStmt *) stmt)->objtype;
19897 else
19898 {
19899 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
19900 reltype = OBJECT_TABLE; /* placate compiler */
19901 }
19902
19903 /*
19904 * For compatibility with prior releases, we allow ALTER TABLE to be used
19905 * with most other types of relations (but not composite types). We allow
19906 * similar flexibility for ALTER INDEX in the case of RENAME, but not
19907 * otherwise. Otherwise, the user must select the correct form of the
19908 * command for the relation at issue.
19909 */
19910 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
19911 ereport(ERROR,
19913 errmsg("\"%s\" is not a sequence", rv->relname)));
19914
19915 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
19916 ereport(ERROR,
19918 errmsg("\"%s\" is not a view", rv->relname)));
19919
19920 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
19921 ereport(ERROR,
19923 errmsg("\"%s\" is not a materialized view", rv->relname)));
19924
19925 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
19926 ereport(ERROR,
19928 errmsg("\"%s\" is not a foreign table", rv->relname)));
19929
19930 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
19931 ereport(ERROR,
19933 errmsg("\"%s\" is not a composite type", rv->relname)));
19934
19935 if (reltype == OBJECT_PROPGRAPH && relkind != RELKIND_PROPGRAPH)
19936 ereport(ERROR,
19938 errmsg("\"%s\" is not a property graph", rv->relname)));
19939
19940 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
19941 relkind != RELKIND_PARTITIONED_INDEX
19942 && !IsA(stmt, RenameStmt))
19943 ereport(ERROR,
19945 errmsg("\"%s\" is not an index", rv->relname)));
19946
19947 /*
19948 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
19949 * TYPE for that.
19950 */
19951 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
19952 ereport(ERROR,
19954 errmsg("\"%s\" is a composite type", rv->relname),
19955 /* translator: %s is an SQL ALTER command */
19956 errhint("Use %s instead.",
19957 "ALTER TYPE")));
19958
19959 /*
19960 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
19961 * to a different schema, such as indexes and TOAST tables.
19962 */
19964 {
19965 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
19966 ereport(ERROR,
19968 errmsg("cannot change schema of index \"%s\"",
19969 rv->relname),
19970 errhint("Change the schema of the table instead.")));
19971 else if (relkind == RELKIND_COMPOSITE_TYPE)
19972 ereport(ERROR,
19974 errmsg("cannot change schema of composite type \"%s\"",
19975 rv->relname),
19976 /* translator: %s is an SQL ALTER command */
19977 errhint("Use %s instead.",
19978 "ALTER TYPE")));
19979 else if (relkind == RELKIND_TOASTVALUE)
19980 ereport(ERROR,
19982 errmsg("cannot change schema of TOAST table \"%s\"",
19983 rv->relname),
19984 errhint("Change the schema of the table instead.")));
19985 }
19986
19987 ReleaseSysCache(tuple);
19988}
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 21857 of file tablecmds.c.

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

19796{
19797 HeapTuple tuple;
19798
19799 /* Nothing to do if the relation was not found. */
19800 if (!OidIsValid(relId))
19801 return;
19802
19803 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19804 if (!HeapTupleIsValid(tuple)) /* should not happen */
19805 elog(ERROR, "cache lookup failed for relation %u", relId);
19806
19809
19810 ReleaseSysCache(tuple);
19811}

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

19760{
19761 char relkind;
19763
19764 /* Nothing to do if the relation was not found. */
19765 if (!OidIsValid(relId))
19766 return;
19767
19768 /*
19769 * If the relation does exist, check whether it's an index. But note that
19770 * the relation might have been dropped between the time we did the name
19771 * lookup and now. In that case, there's nothing to do.
19772 */
19773 relkind = get_rel_relkind(relId);
19774 if (!relkind)
19775 return;
19776 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19777 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19778 ereport(ERROR,
19780 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19781
19782 /* Check permissions */
19784 if (aclresult != ACLCHECK_OK)
19787 relation->relname);
19788}
#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 19818 of file tablecmds.c.

19820{
19821 HeapTuple tuple;
19822
19823 /* Nothing to do if the relation was not found. */
19824 if (!OidIsValid(relId))
19825 return;
19826
19827 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19828 if (!HeapTupleIsValid(tuple)) /* should not happen */
19829 elog(ERROR, "cache lookup failed for relation %u", relId);
19830
19833 relation->relname);
19834
19835 if (!allowSystemTableMods &&
19836 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19837 ereport(ERROR,
19839 errmsg("permission denied: \"%s\" is a system catalog",
19840 relation->relname)));
19841
19842 ReleaseSysCache(tuple);
19843}

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

16117{
16118 CommentStmt *cmd;
16119 char *comment_str;
16121
16122 /* Look for comment for object wanted, and leave if none */
16124 if (comment_str == NULL)
16125 return;
16126
16127 /* Build CommentStmt node, copying all input data for safety */
16128 cmd = makeNode(CommentStmt);
16129 if (rel)
16130 {
16132 cmd->object = (Node *)
16135 makeString(pstrdup(conname)));
16136 }
16137 else
16138 {
16140 cmd->object = (Node *)
16142 makeString(pstrdup(conname)));
16143 }
16144 cmd->comment = comment_str;
16145
16146 /* Append it to list of commands */
16148 newcmd->subtype = AT_ReAddComment;
16149 newcmd->def = (Node *) cmd;
16150 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
16151}
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 22084 of file tablecmds.c.

22085{
22087
22088 existingIdx = index_get_partition(partitionTbl,
22089 RelationGetRelid(parentIdx));
22091 ereport(ERROR,
22093 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22095 RelationGetRelationName(parentIdx)),
22096 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
22098 RelationGetRelationName(partitionTbl))));
22099}

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

19526{
19529
19530 /*
19531 * We needn't bother registering the relation unless there is an ON COMMIT
19532 * action we need to take.
19533 */
19534 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19535 return;
19536
19538
19540 oc->relid = relid;
19541 oc->oncommit = action;
19542 oc->creating_subid = GetCurrentSubTransactionId();
19543 oc->deleting_subid = InvalidSubTransactionId;
19544
19545 /*
19546 * We use lcons() here so that ON COMMIT actions are processed in reverse
19547 * order of registration. That might not be essential but it seems
19548 * reasonable.
19549 */
19551
19553}
List * lcons(void *datum, List *list)
Definition list.c:495
MemoryContext CacheMemoryContext
Definition mcxt.c:169

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

Referenced by heap_create_with_catalog().

◆ relation_mark_replica_identity()

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

Definition at line 18666 of file tablecmds.c.

18668{
18675 ListCell *index;
18676
18677 /*
18678 * Check whether relreplident has changed, and update it if so.
18679 */
18684 elog(ERROR, "cache lookup failed for relation \"%s\"",
18687 if (pg_class_form->relreplident != ri_type)
18688 {
18689 pg_class_form->relreplident = ri_type;
18691 }
18694
18695 /*
18696 * Update the per-index indisreplident flags correctly.
18697 */
18699 foreach(index, RelationGetIndexList(rel))
18700 {
18702 bool dirty = false;
18703
18707 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18709
18710 if (thisIndexOid == indexOid)
18711 {
18712 /* Set the bit if not already set. */
18713 if (!pg_index_form->indisreplident)
18714 {
18715 dirty = true;
18716 pg_index_form->indisreplident = true;
18717 }
18718 }
18719 else
18720 {
18721 /* Unset the bit if set. */
18722 if (pg_index_form->indisreplident)
18723 {
18724 dirty = true;
18725 pg_index_form->indisreplident = false;
18726 }
18727 }
18728
18729 if (dirty)
18730 {
18733 InvalidOid, is_internal);
18734
18735 /*
18736 * Invalidate the relcache for the table, so that after we commit
18737 * all sessions will refresh the table's replica identity index
18738 * before attempting any UPDATE or DELETE on the table. (If we
18739 * changed the table's pg_class row above, then a relcache inval
18740 * is already queued due to that; but we might not have.)
18741 */
18743 }
18745 }
18746
18748}
Definition type.h:96

References CacheInvalidateRelcache(), CatalogTupleUpdate(), elog, ERROR, fb(), Form_pg_index, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvalidOid, InvokeObjectPostAlterHookArg, lfirst_oid, ObjectIdGetDatum(), RelationGetIndexList(), RelationGetRelationName, RelationGetRelid, RowExclusiveLock, SearchSysCacheCopy1, table_close(), and table_open().

Referenced by ATExecReplicaIdentity().

◆ RememberAllDependentForRebuilding()

static void RememberAllDependentForRebuilding ( AlteredTableInfo tab,
AlterTableType  subtype,
Relation  rel,
AttrNumber  attnum,
const char colName 
)
static

Definition at line 15315 of file tablecmds.c.

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

15557{
15558 if (!get_index_isclustered(indoid))
15559 return;
15560
15561 if (tab->clusterOnIndex)
15562 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15563
15564 tab->clusterOnIndex = get_rel_name(indoid);
15565}
bool get_index_isclustered(Oid index_oid)
Definition lsyscache.c:3821

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

15573{
15574 /*
15575 * This de-duplication check is critical for two independent reasons: we
15576 * mustn't try to recreate the same constraint twice, and if a constraint
15577 * depends on more than one column whose type is to be altered, we must
15578 * capture its definition string before applying any of the column type
15579 * changes. ruleutils.c will get confused if we ask again later.
15580 */
15581 if (!list_member_oid(tab->changedConstraintOids, conoid))
15582 {
15583 /* OK, capture the constraint's existing definition string */
15585 Oid indoid;
15586
15587 /*
15588 * It is critical to create not-null constraints ahead of primary key
15589 * indexes; otherwise, the not-null constraint would be created by the
15590 * primary key, and the constraint name would be wrong.
15591 */
15593 {
15594 tab->changedConstraintOids = lcons_oid(conoid,
15598 }
15599 else
15600 {
15601
15603 conoid);
15605 defstring);
15606 }
15607
15608 /*
15609 * For the index of a constraint, if any, remember if it is used for
15610 * the table's replica identity or if it is a clustered index, so that
15611 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15612 * those properties.
15613 */
15614 indoid = get_constraint_index(conoid);
15615 if (OidIsValid(indoid))
15616 {
15618 RememberClusterOnForRebuilding(indoid, tab);
15619 }
15620 }
15621}
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 15628 of file tablecmds.c.

15629{
15630 /*
15631 * This de-duplication check is critical for two independent reasons: we
15632 * mustn't try to recreate the same index twice, and if an index depends
15633 * on more than one column whose type is to be altered, we must capture
15634 * its definition string before applying any of the column type changes.
15635 * ruleutils.c will get confused if we ask again later.
15636 */
15637 if (!list_member_oid(tab->changedIndexOids, indoid))
15638 {
15639 /*
15640 * Before adding it as an index-to-rebuild, we'd better see if it
15641 * belongs to a constraint, and if so rebuild the constraint instead.
15642 * Typically this check fails, because constraint indexes normally
15643 * have only dependencies on their constraint. But it's possible for
15644 * such an index to also have direct dependencies on table columns,
15645 * for example with a partial exclusion constraint.
15646 */
15647 Oid conoid = get_index_constraint(indoid);
15648
15649 if (OidIsValid(conoid))
15650 {
15652 }
15653 else
15654 {
15655 /* OK, capture the index's existing definition string */
15656 char *defstring = pg_get_indexdef_string(indoid);
15657
15659 indoid);
15661 defstring);
15662
15663 /*
15664 * Remember if this index is used for the table's replica identity
15665 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15666 * can queue up commands necessary to restore those properties.
15667 */
15669 RememberClusterOnForRebuilding(indoid, tab);
15670 }
15671 }
15672}
Oid get_index_constraint(Oid indexId)
Definition pg_depend.c:1061
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 15541 of file tablecmds.c.

15542{
15543 if (!get_index_isreplident(indoid))
15544 return;
15545
15546 if (tab->replicaIdentityIndex)
15547 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15548
15549 tab->replicaIdentityIndex = get_rel_name(indoid);
15550}
bool get_index_isreplident(Oid index_oid)
Definition lsyscache.c:3775

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

15680{
15681 /*
15682 * This de-duplication check is critical for two independent reasons: we
15683 * mustn't try to recreate the same statistics object twice, and if the
15684 * statistics object depends on more than one column whose type is to be
15685 * altered, we must capture its definition string before applying any of
15686 * the type changes. ruleutils.c will get confused if we ask again later.
15687 */
15689 {
15690 /* OK, capture the statistics object's existing definition string */
15692
15694 stxoid);
15696 defstring);
15697 }
15698}
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 19561 of file tablecmds.c.

19562{
19563 ListCell *l;
19564
19565 foreach(l, on_commits)
19566 {
19568
19569 if (oc->relid == relid)
19570 {
19572 break;
19573 }
19574 }
19575}
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 18214 of file tablecmds.c.

18215{
18217 SysScanDesc scan;
18218 ScanKeyData key[3];
18221 AttrMap *attmap;
18222 List *connames;
18223 List *nncolumns;
18224 bool found;
18225 bool is_partitioning;
18226
18228
18233 if (!found)
18234 {
18235 if (is_partitioning)
18236 ereport(ERROR,
18238 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18241 else
18242 ereport(ERROR,
18244 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18247 }
18248
18249 /*
18250 * Search through child columns looking for ones matching parent rel
18251 */
18253 ScanKeyInit(&key[0],
18258 true, NULL, 1, key);
18260 {
18262
18263 /* Ignore if dropped or not inherited */
18264 if (att->attisdropped)
18265 continue;
18266 if (att->attinhcount <= 0)
18267 continue;
18268
18270 NameStr(att->attname)))
18271 {
18272 /* Decrement inhcount and possibly set islocal to true */
18275
18276 copy_att->attinhcount--;
18277 if (copy_att->attinhcount == 0)
18278 copy_att->attislocal = true;
18279
18282 }
18283 }
18284 systable_endscan(scan);
18286
18287 /*
18288 * Likewise, find inherited check and not-null constraints and disinherit
18289 * them. To do this, we first need a list of the names of the parent's
18290 * check constraints. (We cheat a bit by only checking for name matches,
18291 * assuming that the expressions will match.)
18292 *
18293 * For NOT NULL columns, we store column numbers to match, mapping them in
18294 * to the child rel's attribute numbers.
18295 */
18298 false);
18299
18301 ScanKeyInit(&key[0],
18306 true, NULL, 1, key);
18307
18308 connames = NIL;
18309 nncolumns = NIL;
18310
18312 {
18314
18315 if (con->connoinherit)
18316 continue;
18317
18318 if (con->contype == CONSTRAINT_CHECK)
18319 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18320 if (con->contype == CONSTRAINT_NOTNULL)
18321 {
18323
18325 }
18326 }
18327
18328 systable_endscan(scan);
18329
18330 /* Now scan the child's constraints to find matches */
18331 ScanKeyInit(&key[0],
18336 true, NULL, 1, key);
18337
18339 {
18341 bool match = false;
18342
18343 /*
18344 * Match CHECK constraints by name, not-null constraints by column
18345 * number, and ignore all others.
18346 */
18347 if (con->contype == CONSTRAINT_CHECK)
18348 {
18350 {
18351 if (con->contype == CONSTRAINT_CHECK &&
18352 strcmp(NameStr(con->conname), chkname) == 0)
18353 {
18354 match = true;
18356 break;
18357 }
18358 }
18359 }
18360 else if (con->contype == CONSTRAINT_NOTNULL)
18361 {
18363
18365 {
18366 if (prevattno == child_attno)
18367 {
18368 match = true;
18370 break;
18371 }
18372 }
18373 }
18374 else
18375 continue;
18376
18377 if (match)
18378 {
18379 /* Decrement inhcount and possibly set islocal to true */
18382
18383 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18384 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18386
18387 copy_con->coninhcount--;
18388 if (copy_con->coninhcount == 0)
18389 copy_con->conislocal = true;
18390
18393 }
18394 }
18395
18396 /* We should have matched all constraints */
18397 if (connames != NIL || nncolumns != NIL)
18398 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18401
18402 systable_endscan(scan);
18404
18409
18410 /*
18411 * Post alter hook of this inherits. Since object_access_hook doesn't take
18412 * multiple object identifiers, we relay oid of parent relation using
18413 * auxiliary_id argument.
18414 */
18418}
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 12024 of file tablecmds.c.

12026{
12030 SysScanDesc scan;
12032
12033 ScanKeyInit(&key,
12036 ObjectIdGetDatum(conrelid));
12037
12040 true, NULL, 1, &key);
12042 while ((consttup = systable_getnext(scan)) != NULL)
12043 {
12045
12046 if (conform->conparentid != conoid)
12047 continue;
12048 else
12049 {
12050 ObjectAddress addr;
12054
12057
12058 /*
12059 * First we must delete the dependency record that binds the
12060 * constraint records together.
12061 */
12063 conform->oid,
12066 conoid);
12067 Assert(n == 1); /* actually only one is expected */
12068
12069 /*
12070 * Now search for the triggers for this constraint and set them up
12071 * for deletion too
12072 */
12078 true, NULL, 1, &key2);
12079 while ((trigtup = systable_getnext(scan2)) != NULL)
12080 {
12084 }
12086 }
12087 }
12088 /* make the dependency deletions visible */
12092 systable_endscan(scan);
12093}
#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:400

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

Referenced by AttachPartitionForeignKey().

◆ RemoveRelations()

void RemoveRelations ( DropStmt drop)

Definition at line 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:3529

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

7957{
7958 Form_pg_attribute attr;
7960
7962
7964
7965 /*
7966 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7967 * attribute.
7968 */
7969 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7970 if (attr->attisdropped)
7971 return;
7972
7973 if (!attr->attnotnull)
7974 {
7976 HeapTuple tuple;
7977
7979
7981 if (!HeapTupleIsValid(tuple))
7982 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
7983 attnum, RelationGetRelid(rel));
7984
7986 thisatt->attnullability = ATTNULLABLE_VALID;
7987
7988 attr = (Form_pg_attribute) GETSTRUCT(tuple);
7989
7990 attr->attnotnull = true;
7991 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
7992
7993 /*
7994 * If the nullness isn't already proven by validated constraints, have
7995 * ALTER TABLE phase 3 test for it.
7996 */
7997 if (queue_validation && wqueue &&
7999 {
8000 AlteredTableInfo *tab;
8001
8002 tab = ATGetQueueEntry(wqueue, rel);
8003 tab->verify_new_notnull = true;
8004 }
8005
8007
8009 heap_freetuple(tuple);
8010 }
8011 else
8012 {
8014 }
8015}
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition tablecmds.c:8204

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

9237{
9238 ListCell *lc;
9239
9240 foreach(lc, RelationGetIndexList(rel))
9241 {
9242 Oid indexoid = lfirst_oid(lc);
9245 HeapTuple tuple;
9246
9247 indrel = index_open(indexoid, lockmode);
9248
9249 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9250 {
9251 if (indrel->rd_index->indkey.values[i] == attnum)
9252 {
9253 indattnum = i + 1;
9254 break;
9255 }
9256 }
9257
9258 if (indattnum == 0)
9259 {
9260 index_close(indrel, lockmode);
9261 continue;
9262 }
9263
9265
9266 if (HeapTupleIsValid(tuple))
9267 {
9269
9270 if (setstorage)
9271 attrtuple->attstorage = newstorage;
9272
9273 if (setcompression)
9274 attrtuple->attcompression = newcompression;
9275
9276 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9277
9279 RelationGetRelid(rel),
9280 attrtuple->attnum);
9281
9282 heap_freetuple(tuple);
9283 }
9284
9285 index_close(indrel, lockmode);
9286 }
9287}

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:1669

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

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

◆ SetRelationTableSpace()

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

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

23542{
23543 /* The FSM is empty, so don't bother using it. */
23544 uint32 ti_options = TABLE_INSERT_SKIP_FSM;
23545 CommandId mycid;
23546 EState *estate;
23548 *listptr2;
23550 ExprContext *econtext;
23551 TableScanDesc scan;
23552 Snapshot snapshot;
23557 *pc;
23558
23559 mycid = GetCurrentCommandId(true);
23560
23561 estate = CreateExecutorState();
23562
23563 forboth(listptr, partlist, listptr2, newPartRels)
23564 {
23566
23568
23569 /* Find the work queue entry for the new partition table: newPartRel. */
23570 pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
23571
23572 buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
23573
23574 if (sps->bound->is_default)
23575 {
23576 /*
23577 * We should not create a structure to check the partition
23578 * constraint for the new DEFAULT partition.
23579 */
23581 }
23582 else
23583 {
23585
23586 /* Build expression execution states for partition check quals. */
23590 (Node *) partConstraint);
23591 /* Make a boolean expression for ExecCheck(). */
23593
23594 /*
23595 * Map the vars in the constraint expression from rel's attnos to
23596 * splitRel's.
23597 */
23599 1, splitRel, rel);
23600
23601 pc->partqualstate =
23603 Assert(pc->partqualstate != NULL);
23604 }
23605
23606 /* Store partition context into a list. */
23608 }
23609
23610 econtext = GetPerTupleExprContext(estate);
23611
23612 /* Create the necessary tuple slot. */
23614
23615 /*
23616 * Map computing for moving attributes of the split partition to the new
23617 * partition (for the first new partition, but other new partitions can
23618 * use the same map).
23619 */
23622 RelationGetDescr(pc->partRel));
23623
23624 /* Scan through the rows. */
23625 snapshot = RegisterSnapshot(GetLatestSnapshot());
23626 scan = table_beginscan(splitRel, snapshot, 0, NULL,
23627 SO_NONE);
23628
23629 /*
23630 * Switch to per-tuple memory context and reset it for each tuple
23631 * produced, so we don't leak memory.
23632 */
23634
23636 {
23637 bool found = false;
23639
23641
23642 econtext->ecxt_scantuple = srcslot;
23643
23644 /* Search partition for the current slot, srcslot. */
23645 foreach(listptr, partContexts)
23646 {
23648
23649 /* skip DEFAULT partition */
23650 if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
23651 {
23652 found = true;
23653 break;
23654 }
23655 }
23656 if (!found)
23657 {
23658 /* Use the DEFAULT partition if it exists. */
23659 if (defaultPartCtx)
23661 else
23662 ereport(ERROR,
23664 errmsg("can not find partition for split partition row"),
23666 }
23667
23668 if (tuple_map)
23669 {
23670 /* Need to use a map to copy attributes. */
23671 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
23672 }
23673 else
23674 {
23675 /* Extract data from the old tuple. */
23677
23678 /* Copy attributes directly. */
23679 insertslot = pc->dstslot;
23680
23682
23683 memcpy(insertslot->tts_values, srcslot->tts_values,
23684 sizeof(Datum) * srcslot->tts_nvalid);
23685 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23686 sizeof(bool) * srcslot->tts_nvalid);
23687
23689 }
23690
23691 /*
23692 * Constraints and GENERATED expressions might reference the tableoid
23693 * column, so fill tts_tableOid with the desired value. (We must do
23694 * this each time, because it gets overwritten with newrel's OID
23695 * during storing.)
23696 */
23697 insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
23698
23699 /*
23700 * Now, evaluate any generated expressions whose inputs come from the
23701 * new tuple. We assume these columns won't reference each other, so
23702 * that there's no ordering dependency.
23703 */
23705 insertslot, econtext);
23706
23707 /* Write the tuple out to the new relation. */
23708 table_tuple_insert(pc->partRel, insertslot, mycid,
23709 ti_options, pc->bistate);
23710
23711 ResetExprContext(econtext);
23712 }
23713
23715
23716 table_endscan(scan);
23717 UnregisterSnapshot(snapshot);
23718
23719 if (tuple_map)
23721
23723
23724 FreeExecutorState(estate);
23725
23728}
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 13599 of file tablecmds.c.

13601{
13602 ListCell *l;
13603 int attnum;
13604
13605 attnum = 0;
13606 foreach(l, colList)
13607 {
13608 char *attname = strVal(lfirst(l));
13611
13614 ereport(ERROR,
13616 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13617 attname)));
13619 if (attform->attnum < 0)
13620 ereport(ERROR,
13622 errmsg("system columns cannot be used in foreign keys")));
13623 if (attnum >= INDEX_MAX_KEYS)
13624 ereport(ERROR,
13626 errmsg("cannot have more than %d keys in a foreign key",
13627 INDEX_MAX_KEYS)));
13628 attnums[attnum] = attform->attnum;
13629 if (atttypids != NULL)
13630 atttypids[attnum] = attform->atttypid;
13631 if (attcollids != NULL)
13632 attcollids[attnum] = attform->attcollation;
13634 attnum++;
13635 }
13636
13637 return attnum;
13638}

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

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

13658{
13665 int i;
13666
13667 /*
13668 * Get the list of index OIDs for the table from the relcache, and look up
13669 * each one in the pg_index syscache until we find one marked primary key
13670 * (hopefully there isn't more than one such). Insist it's valid, too.
13671 */
13672 *indexOid = InvalidOid;
13673
13675
13676 foreach(indexoidscan, indexoidlist)
13677 {
13678 Oid indexoid = lfirst_oid(indexoidscan);
13679
13682 elog(ERROR, "cache lookup failed for index %u", indexoid);
13684 if (indexStruct->indisprimary && indexStruct->indisvalid)
13685 {
13686 /*
13687 * Refuse to use a deferrable primary key. This is per SQL spec,
13688 * and there would be a lot of interesting semantic problems if we
13689 * tried to allow it.
13690 */
13691 if (!indexStruct->indimmediate)
13692 ereport(ERROR,
13694 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13696
13697 *indexOid = indexoid;
13698 break;
13699 }
13701 }
13702
13704
13705 /*
13706 * Check that we found it
13707 */
13708 if (!OidIsValid(*indexOid))
13709 ereport(ERROR,
13711 errmsg("there is no primary key for referenced table \"%s\"",
13713
13714 /* Must get indclass the hard way */
13718
13719 /*
13720 * Now build the list of PK attributes from the indkey definition (we
13721 * assume a primary key cannot have expressional elements)
13722 */
13723 *attnamelist = NIL;
13724 for (i = 0; i < indexStruct->indnkeyatts; i++)
13725 {
13726 int pkattno = indexStruct->indkey.values[i];
13727
13728 attnums[i] = pkattno;
13731 opclasses[i] = indclass->values[i];
13734 }
13735
13736 *pk_has_without_overlaps = indexStruct->indisexclusion;
13737
13739
13740 return i;
13741}
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 19996 of file tablecmds.c.

19997{
19999 ParseState *pstate;
20001 ListCell *l;
20002
20004
20005 newspec->strategy = partspec->strategy;
20006 newspec->partParams = NIL;
20007 newspec->location = partspec->location;
20008
20009 /* Check valid number of columns for strategy */
20010 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
20011 list_length(partspec->partParams) != 1)
20012 ereport(ERROR,
20014 errmsg("cannot use \"list\" partition strategy with more than one column")));
20015
20016 /*
20017 * Create a dummy ParseState and insert the target relation as its sole
20018 * rangetable entry. We need a ParseState for transformExpr.
20019 */
20020 pstate = make_parsestate(NULL);
20022 NULL, false, true);
20023 addNSItemToQuery(pstate, nsitem, true, true, true);
20024
20025 /* take care of any partition expressions */
20026 foreach(l, partspec->partParams)
20027 {
20029
20030 if (pelem->expr)
20031 {
20032 /* Copy, to avoid scribbling on the input */
20034
20035 /* Now do parse transformation of the expression */
20036 pelem->expr = transformExpr(pstate, pelem->expr,
20038
20039 /* we have to fix its collations too */
20040 assign_expr_collations(pstate, pelem->expr);
20041 }
20042
20043 newspec->partParams = lappend(newspec->partParams, pelem);
20044 }
20045
20046 return newspec;
20047}
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 11797 of file tablecmds.c.

11808{
11813
11817 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11819
11820 /*
11821 * Do some quick & easy initial checks. If any of these fail, we cannot
11822 * use this constraint.
11823 */
11824 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11825 {
11827 return false;
11828 }
11829 for (int i = 0; i < numfks; i++)
11830 {
11831 if (fk->conkey[i] != mapped_conkey[i] ||
11832 fk->confkey[i] != confkey[i] ||
11833 fk->conpfeqop[i] != conpfeqop[i])
11834 {
11836 return false;
11837 }
11838 }
11839
11840 /* Looks good so far; perform more extensive checks. */
11843 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11845
11846 /*
11847 * An error should be raised if the constraint enforceability is
11848 * different. Returning false without raising an error, as we do for other
11849 * attributes, could lead to a duplicate constraint with the same
11850 * enforceability as the parent. While this may be acceptable, it may not
11851 * be ideal. Therefore, it's better to raise an error and allow the user
11852 * to correct the enforceability before proceeding.
11853 */
11854 if (partConstr->conenforced != parentConstr->conenforced)
11855 ereport(ERROR,
11857 errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11858 NameStr(parentConstr->conname),
11859 NameStr(partConstr->conname),
11861
11862 if (OidIsValid(partConstr->conparentid) ||
11863 partConstr->condeferrable != parentConstr->condeferrable ||
11864 partConstr->condeferred != parentConstr->condeferred ||
11865 partConstr->confupdtype != parentConstr->confupdtype ||
11866 partConstr->confdeltype != parentConstr->confdeltype ||
11867 partConstr->confmatchtype != parentConstr->confmatchtype)
11868 {
11871 return false;
11872 }
11873
11876
11877 /* Looks good! Attach this constraint. */
11881
11882 return true;
11883}
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 16187 of file tablecmds.c.

16188{
16189 HeapTuple tup;
16190 Datum adatum;
16191 ArrayType *arr;
16192 Oid *rawarr;
16193 int numkeys;
16194 int i;
16195
16196 Assert(con->contype == CONSTR_FOREIGN);
16197 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
16198
16200 if (!HeapTupleIsValid(tup)) /* should not happen */
16201 elog(ERROR, "cache lookup failed for constraint %u", oldId);
16202
16205 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
16206 numkeys = ARR_DIMS(arr)[0];
16207 /* test follows the one in ri_FetchConstraintInfo() */
16208 if (ARR_NDIM(arr) != 1 ||
16209 ARR_HASNULL(arr) ||
16210 ARR_ELEMTYPE(arr) != OIDOID)
16211 elog(ERROR, "conpfeqop is not a 1-D Oid array");
16212 rawarr = (Oid *) ARR_DATA_PTR(arr);
16213
16214 /* stash a List of the operator Oids in our Constraint node */
16215 for (i = 0; i < numkeys; i++)
16217
16219}
#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 16158 of file tablecmds.c.

16159{
16161 stmt->accessMethod,
16162 stmt->indexParams,
16163 stmt->excludeOpNames,
16164 stmt->iswithoutoverlaps))
16165 {
16167
16168 /* If it's a partitioned index, there is no storage to share. */
16169 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
16170 {
16171 stmt->oldNumber = irel->rd_locator.relNumber;
16172 stmt->oldCreateSubid = irel->rd_createSubid;
16173 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
16174 }
16175 index_close(irel, NoLock);
16176 }
16177}
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 10749 of file tablecmds.c.

10752{
10753 int numcolsout = 0;
10754
10755 for (int i = 0; i < numfksetcols; i++)
10756 {
10758 bool seen = false;
10759
10760 /* Make sure it's in fkattnums[] */
10761 for (int j = 0; j < numfks; j++)
10762 {
10763 if (fkattnums[j] == setcol_attnum)
10764 {
10765 seen = true;
10766 break;
10767 }
10768 }
10769
10770 if (!seen)
10771 {
10772 char *col = strVal(list_nth(fksetcols, i));
10773
10774 ereport(ERROR,
10776 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10777 }
10778
10779 /* Now check for dups */
10780 seen = false;
10781 for (int j = 0; j < numcolsout; j++)
10782 {
10784 {
10785 seen = true;
10786 break;
10787 }
10788 }
10789 if (!seen)
10791 }
10792 return numcolsout;
10793}

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

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

22109{
22111 SysScanDesc scan;
22113 int tuples = 0;
22115 bool updated = false;
22116
22117 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
22118
22119 /*
22120 * Scan pg_inherits for this parent index. Count each valid index we find
22121 * (verifying the pg_index entry for each), and if we reach the total
22122 * amount we expect, we can mark this parent index as valid.
22123 */
22129 NULL, 1, &key);
22130 while ((inhTup = systable_getnext(scan)) != NULL)
22131 {
22135
22137 ObjectIdGetDatum(inhForm->inhrelid));
22139 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
22141 if (indexForm->indisvalid)
22142 tuples += 1;
22144 }
22145
22146 /* Done with pg_inherits */
22147 systable_endscan(scan);
22149
22150 /*
22151 * If we found as many inherited indexes as the partitioned table has
22152 * partitions, we're good; update pg_index to set indisvalid.
22153 */
22154 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
22155 {
22159
22164 elog(ERROR, "cache lookup failed for index %u",
22167
22168 indexForm->indisvalid = true;
22169 updated = true;
22170
22172
22175 }
22176
22177 /*
22178 * If this index is in turn a partition of a larger index, validating it
22179 * might cause the parent to become valid also. Try that.
22180 */
22181 if (updated && partedIdx->rd_rel->relispartition)
22182 {
22185 Relation parentIdx,
22186 parentTbl;
22187
22188 /* make sure we see the validation we just did */
22190
22195 Assert(!parentIdx->rd_index->indisvalid);
22196
22197 validatePartitionedIndex(parentIdx, parentTbl);
22198
22201 }
22202}

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

9681{
9683
9684 if (conForm->contype != CONSTRAINT_NOTNULL)
9685 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9686
9687 /* a NO INHERIT constraint is no good */
9688 if (conForm->connoinherit)
9689 ereport(ERROR,
9691 errmsg("cannot create primary key on column \"%s\"", colname),
9692 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9693 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9694 NameStr(conForm->conname), colname,
9695 get_rel_name(conForm->conrelid), "NO INHERIT"),
9696 errhint("You might need to make the existing constraint inheritable using %s.",
9697 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9698
9699 /* an unvalidated constraint is no good */
9700 if (!conForm->convalidated)
9701 ereport(ERROR,
9703 errmsg("cannot create primary key on column \"%s\"", colname),
9704 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9705 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9706 NameStr(conForm->conname), colname,
9707 get_rel_name(conForm->conrelid), "NOT VALID"),
9708 errhint("You might need to validate it using %s.",
9709 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9710}

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

22211{
22212 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
22213 {
22215 iinfo->ii_IndexAttrNumbers[i] - 1);
22216
22217 if (!att->attnotnull)
22218 ereport(ERROR,
22220 errmsg("invalid primary key definition"),
22221 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
22222 NameStr(att->attname),
22224 }
22225}

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