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_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_policy.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_publication_rel.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_type.h"
#include "catalog/storage.h"
#include "catalog/storage_xlog.h"
#include "catalog/toasting.h"
#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "commands/user.h"
#include "commands/vacuum.h"
#include "common/int.h"
#include "executor/executor.h"
#include "foreign/fdwapi.h"
#include "foreign/foreign.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "nodes/parsenodes.h"
#include "optimizer/optimizer.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
#include "parser/parse_type.h"
#include "parser/parse_utilcmd.h"
#include "parser/parser.h"
#include "partitioning/partbounds.h"
#include "partitioning/partdesc.h"
#include "pgstat.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "storage/bufmgr.h"
#include "storage/lmgr.h"
#include "storage/lock.h"
#include "storage/predicate.h"
#include "storage/smgr.h"
#include "tcop/utility.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/partcache.h"
#include "utils/relcache.h"
#include "utils/ruleutils.h"
#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
#include "utils/usercontext.h"
Include dependency graph for tablecmds.c:

Go to the source code of this file.

Data Structures

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

Macros

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

Typedefs

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

Enumerations

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

Functions

static void truncate_check_rel (Oid relid, Form_pg_class reltuple)
 
static void truncate_check_perms (Oid relid, Form_pg_class reltuple)
 
static void truncate_check_activity (Relation rel)
 
static void RangeVarCallbackForTruncate (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
static ListMergeAttributes (List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
 
static ListMergeCheckConstraint (List *constraints, const char *name, Node *expr, bool is_enforced)
 
static void MergeChildAttribute (List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
 
static ColumnDefMergeInheritedAttribute (List *inh_columns, int exist_attno, const ColumnDef *newdef)
 
static void MergeAttributesIntoExisting (Relation child_rel, Relation parent_rel, bool ispartition)
 
static void MergeConstraintsIntoExisting (Relation child_rel, Relation parent_rel)
 
static void StoreCatalogInheritance (Oid relationId, List *supers, bool child_is_partition)
 
static void StoreCatalogInheritance1 (Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
 
static int findAttrByName (const char *attributeName, const List *columns)
 
static void AlterIndexNamespaces (Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
 
static void AlterSeqNamespaces (Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
 
static ObjectAddress ATExecAlterConstraint (List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
 
static bool ATExecAlterConstraintInternal (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)
 
static bool 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)
 
ObjectAddress DefineRelation (CreateStmt *stmt, char relkind, Oid ownerId, ObjectAddress *typaddress, const char *queryString)
 
TupleDesc BuildDescForRelation (const List *columns)
 
static void DropErrorMsgNonExistent (RangeVar *rel, char rightkind, bool missing_ok)
 
static void DropErrorMsgWrongType (const char *relname, char wrongkind, char rightkind)
 
void RemoveRelations (DropStmt *drop)
 
void ExecuteTruncate (TruncateStmt *stmt)
 
void ExecuteTruncateGuts (List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
 
void SetRelationHasSubclass (Oid relationId, bool relhassubclass)
 
bool CheckRelationTableSpaceMove (Relation rel, Oid newTableSpaceId)
 
void SetRelationTableSpace (Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
 
static void renameatt_check (Oid myrelid, Form_pg_class classform, bool recursing)
 
static AttrNumber renameatt_internal (Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
 
static void RangeVarCallbackForRenameAttribute (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
ObjectAddress renameatt (RenameStmt *stmt)
 
static ObjectAddress rename_constraint_internal (Oid myrelid, Oid mytypid, const char *oldconname, const char *newconname, bool recurse, bool recursing, int expected_parents)
 
ObjectAddress RenameConstraint (RenameStmt *stmt)
 
ObjectAddress RenameRelation (RenameStmt *stmt)
 
void RenameRelationInternal (Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
 
void ResetRelRewrite (Oid myrelid)
 
void CheckTableNotInUse (Relation rel, const char *stmt)
 
Oid AlterTableLookupRelation (AlterTableStmt *stmt, LOCKMODE lockmode)
 
void AlterTable (AlterTableStmt *stmt, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
void AlterTableInternal (Oid relid, List *cmds, bool recurse)
 
LOCKMODE AlterTableGetLockLevel (List *cmds)
 
static const charalter_table_type_to_string (AlterTableType cmdtype)
 
void find_composite_type_dependencies (Oid typeOid, Relation origRelation, const char *origTypeName)
 
void check_of_type (HeapTuple typetuple)
 
static void SetIndexStorageProperties (Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
 
static Oid CreateFKCheckTrigger (Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
 
static void RememberReplicaIdentityForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
static void RememberClusterOnForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
void ATExecChangeOwner (Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
 
Oid AlterTableMoveAll (AlterTableMoveAllStmt *stmt)
 
static chardecompile_conbin (HeapTuple contup, TupleDesc tupdesc)
 
static bool constraints_equivalent (HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
 
static void MarkInheritDetached (Relation child_rel, Relation parent_rel)
 
static void relation_mark_replica_identity (Relation rel, char ri_type, Oid indexOid, bool is_internal)
 
ObjectAddress AlterTableNamespace (AlterObjectSchemaStmt *stmt, Oid *oldschema)
 
void AlterTableNamespaceInternal (Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
 
void AlterRelationNamespaceInternal (Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
 
void register_on_commit_action (Oid relid, OnCommitAction action)
 
void remove_on_commit_action (Oid relid)
 
void PreCommit_on_commit_actions (void)
 
void AtEOXact_on_commit_actions (bool isCommit)
 
void AtEOSubXact_on_commit_actions (bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
 
void RangeVarCallbackMaintainsTable (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
void RangeVarCallbackOwnsRelation (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
bool PartConstraintImpliedByRelConstraint (Relation scanrel, List *partConstraint)
 
static void attachPartitionTable (List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
 
static void RangeVarCallbackForAttachIndex (const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
 
static void buildExpressionExecutionStates (AlteredTableInfo *tab, Relation newPartRel, EState *estate)
 
static void evaluateGeneratedExpressionsAndCheckConstraints (AlteredTableInfo *tab, Relation newPartRel, TupleTableSlot *insertslot, ExprContext *econtext)
 
static ListgetAttributesList (Relation parent_rel)
 
static void createTableConstraints (List **wqueue, AlteredTableInfo *tab, Relation parent_rel, Relation newRel)
 
static Relation createPartitionTable (List **wqueue, RangeVar *newPartName, Relation parent_rel, Oid ownerId)
 
static void MergePartitionsMoveRows (List **wqueue, List *mergingPartitions, Relation newPartRel)
 
static void detachPartitionTable (Relation parent_rel, Relation child_rel, Oid defaultPartOid)
 
static SplitPartitionContextcreateSplitPartitionContext (Relation partRel)
 
static void deleteSplitPartitionContext (SplitPartitionContext *pc, List **wqueue, int ti_options)
 
static void SplitPartitionMoveRows (List **wqueue, Relation rel, Relation splitRel, List *partlist, List *newPartRels)
 

Variables

static Liston_commits = NIL
 
static const struct dropmsgstrings dropmsgstringarray []
 

Macro Definition Documentation

◆ AT_NUM_PASSES

#define AT_NUM_PASSES   (AT_PASS_MISC + 1)

Definition at line 168 of file tablecmds.c.

◆ ATT_COMPOSITE_TYPE

#define ATT_COMPOSITE_TYPE   0x0010

Definition at line 340 of file tablecmds.c.

◆ ATT_FOREIGN_TABLE

#define ATT_FOREIGN_TABLE   0x0020

Definition at line 341 of file tablecmds.c.

◆ ATT_INDEX

#define ATT_INDEX   0x0008

Definition at line 339 of file tablecmds.c.

◆ ATT_MATVIEW

#define ATT_MATVIEW   0x0004

Definition at line 338 of file tablecmds.c.

◆ ATT_PARTITIONED_INDEX

#define ATT_PARTITIONED_INDEX   0x0040

Definition at line 342 of file tablecmds.c.

◆ ATT_PARTITIONED_TABLE

#define ATT_PARTITIONED_TABLE   0x0100

Definition at line 344 of file tablecmds.c.

◆ ATT_SEQUENCE

#define ATT_SEQUENCE   0x0080

Definition at line 343 of file tablecmds.c.

◆ ATT_TABLE

#define ATT_TABLE   0x0001

Definition at line 336 of file tablecmds.c.

◆ ATT_VIEW

#define ATT_VIEW   0x0002

Definition at line 337 of file tablecmds.c.

◆ child_dependency_type

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

Definition at line 373 of file tablecmds.c.

@ DEPENDENCY_NORMAL
Definition dependency.h:33

Typedef Documentation

◆ addFkConstraintSides

◆ AlteredTableInfo

◆ AlterTablePass

◆ ForeignTruncateInfo

◆ NewColumnValue

◆ NewConstraint

◆ OnCommitItem

◆ SplitPartitionContext

Enumeration Type Documentation

◆ addFkConstraintSides

Enumerator
addFkReferencedSide 
addFkReferencingSide 
addFkBothSides 

Definition at line 361 of file tablecmds.c.

362{
addFkConstraintSides
Definition tablecmds.c:362
@ addFkReferencingSide
Definition tablecmds.c:364
@ addFkBothSides
Definition tablecmds.c:365
@ addFkReferencedSide
Definition tablecmds.c:363

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

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

Function Documentation

◆ add_column_collation_dependency()

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

Definition at line 7804 of file tablecmds.c.

7805{
7807 referenced;
7808
7809 /* We know the default collation is pinned, so don't bother recording it */
7811 {
7813 myself.objectId = relid;
7814 myself.objectSubId = attnum;
7816 referenced.objectId = collid;
7817 referenced.objectSubId = 0;
7819 }
7820}
#define OidIsValid(objectId)
Definition c.h:860
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 7786 of file tablecmds.c.

7787{
7789 referenced;
7790
7792 myself.objectId = relid;
7793 myself.objectSubId = attnum;
7794 referenced.classId = TypeRelationId;
7795 referenced.objectId = typid;
7796 referenced.objectSubId = 0;
7798}

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

10804{
10805 ObjectAddress address;
10806 Oid constrOid;
10807 char *conname;
10808 bool conislocal;
10810 bool connoinherit;
10811
10812 /*
10813 * Verify relkind for each referenced partition. At the top level, this
10814 * is redundant with a previous check, but we need it when recursing.
10815 */
10816 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10817 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10818 ereport(ERROR,
10820 errmsg("referenced relation \"%s\" is not a table",
10822
10823 /*
10824 * Caller supplies us with a constraint name; however, it may be used in
10825 * this partition, so come up with a different one in that case. Unless
10826 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10827 * supplied name with an underscore and digit(s) appended.
10828 */
10830 RelationGetRelid(rel),
10831 constraintname))
10832 conname = ChooseConstraintName(constraintname,
10833 NULL,
10834 "",
10836 else
10837 conname = constraintname;
10838
10839 if (fkconstraint->conname == NULL)
10840 fkconstraint->conname = pstrdup(conname);
10841
10843 {
10844 conislocal = false;
10845 coninhcount = 1;
10846 connoinherit = false;
10847 }
10848 else
10849 {
10850 conislocal = true;
10851 coninhcount = 0;
10852
10853 /*
10854 * always inherit for partitioned tables, never for legacy inheritance
10855 */
10857 }
10858
10859 /*
10860 * Record the FK constraint in pg_constraint.
10861 */
10865 fkconstraint->deferrable,
10866 fkconstraint->initdeferred,
10867 fkconstraint->is_enforced,
10868 fkconstraint->initially_valid,
10870 RelationGetRelid(rel),
10871 fkattnum,
10872 numfks,
10873 numfks,
10874 InvalidOid, /* not a domain constraint */
10875 indexOid,
10877 pkattnum,
10881 numfks,
10882 fkconstraint->fk_upd_action,
10883 fkconstraint->fk_del_action,
10886 fkconstraint->fk_matchtype,
10887 NULL, /* no exclusion constraint */
10888 NULL, /* no check constraint */
10889 NULL,
10890 conislocal, /* islocal */
10891 coninhcount, /* inhcount */
10892 connoinherit, /* conNoInherit */
10893 with_period, /* conPeriod */
10894 is_internal); /* is_internal */
10895
10897
10898 /*
10899 * In partitioning cases, create the dependency entries for this
10900 * constraint. (For non-partitioned cases, relevant entries were created
10901 * by CreateConstraintEntry.)
10902 *
10903 * On the referenced side, we need the constraint to have an internal
10904 * dependency on its parent constraint; this means that this constraint
10905 * cannot be dropped on its own -- only through the parent constraint. It
10906 * also means the containing partition cannot be dropped on its own, but
10907 * it can be detached, at which point this dependency is removed (after
10908 * verifying that no rows are referenced via this FK.)
10909 *
10910 * When processing the referencing side, we link the constraint via the
10911 * special partitioning dependencies: the parent constraint is the primary
10912 * dependent, and the partition on which the foreign key exists is the
10913 * secondary dependency. That way, this constraint is dropped if either
10914 * of these objects is.
10915 *
10916 * Note that this is only necessary for the subsidiary pg_constraint rows
10917 * in partitions; the topmost row doesn't need any of this.
10918 */
10920 {
10922
10924
10928 else
10929 {
10933 }
10934 }
10935
10936 /* make new constraint visible, in case we add more */
10938
10939 return address;
10940}
#define Assert(condition)
Definition c.h:945
int16_t int16
Definition c.h:613
@ DEPENDENCY_INTERNAL
Definition dependency.h:35
@ DEPENDENCY_PARTITION_PRI
Definition dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition dependency.h:37
int errcode(int sqlerrcode)
Definition elog.c:874
#define ERROR
Definition elog.h:39
#define ereport(elevel,...)
Definition elog.h:150
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:514
#define RelationGetRelationName(relation)
Definition rel.h:548
#define RelationGetNamespace(relation)
Definition rel.h:555
Form_pg_class rd_rel
Definition rel.h:111
void CommandCounterIncrement(void)
Definition xact.c:1102

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

10984{
10987
10990
10991 /*
10992 * Create action triggers to enforce the constraint, or skip them if the
10993 * constraint is NOT ENFORCED.
10994 */
10995 if (fkconstraint->is_enforced)
10999 parentConstr, indexOid,
11002
11003 /*
11004 * If the referenced table is partitioned, recurse on ourselves to handle
11005 * each partition. We need one pg_constraint row created for each
11006 * partition in addition to the pg_constraint row for the parent table.
11007 */
11008 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11009 {
11011
11012 for (int i = 0; i < pd->nparts; i++)
11013 {
11014 Relation partRel;
11015 AttrMap *map;
11018 ObjectAddress address;
11019
11020 /* XXX would it be better to acquire these locks beforehand? */
11021 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
11022
11023 /*
11024 * Map the attribute numbers in the referenced side of the FK
11025 * definition to match the partition's column layout.
11026 */
11029 false);
11030 if (map)
11031 {
11033 for (int j = 0; j < numfks; j++)
11034 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
11035 }
11036 else
11038
11039 /* Determine the index to use at this level */
11040 partIndexId = index_get_partition(partRel, indexOid);
11041 if (!OidIsValid(partIndexId))
11042 elog(ERROR, "index for %u not found in partition %s",
11043 indexOid, RelationGetRelationName(partRel));
11044
11045 /* Create entry at this level ... */
11047 fkconstraint->conname, fkconstraint, rel,
11048 partRel, partIndexId, parentConstr,
11052 fkdelsetcols, true, with_period);
11053 /* ... and recurse to our children */
11055 partIndexId, address.objectId, numfks,
11061 with_period);
11062
11063 /* Done -- clean up (but keep the lock) */
11064 table_close(partRel, NoLock);
11065 if (map)
11066 {
11068 free_attrmap(map);
11069 }
11070 }
11071 }
11072}
void free_attrmap(AttrMap *map)
Definition attmap.c:56
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition attmap.c:261
int16 AttrNumber
Definition attnum.h:21
#define elog(elevel,...)
Definition elog.h:226
#define palloc_array(type, count)
Definition fe_memutils.h:76
int j
Definition isn.c:78
int i
Definition isn.c:77
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
Definition lmgr.c:334
#define NoLock
Definition lockdefs.h:34
#define ShareRowExclusiveLock
Definition lockdefs.h:41
void pfree(void *pointer)
Definition mcxt.c:1616
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition partdesc.c:71
Oid index_get_partition(Relation partition, Oid indexId)
Definition partition.c:176
#define RelationGetDescr(relation)
Definition rel.h:540
AttrNumber * attnums
Definition attmap.h:36
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
static ObjectAddress addFkConstraint(addFkConstraintSides fkside, char *constraintname, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool is_internal, bool with_period)
static void addFkRecurseReferenced(Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, Oid parentDelTrigger, Oid parentUpdTrigger, bool with_period)

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

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

◆ addFkRecurseReferencing()

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

Definition at line 11113 of file tablecmds.c.

11121{
11124
11128
11129 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11130 ereport(ERROR,
11132 errmsg("foreign key constraints are not supported on foreign tables")));
11133
11134 /*
11135 * Add check triggers if the constraint is ENFORCED, and if needed,
11136 * schedule them to be checked in Phase 3.
11137 *
11138 * If the relation is partitioned, drill down to do it to its partitions.
11139 */
11140 if (fkconstraint->is_enforced)
11145 indexOid,
11148
11149 if (rel->rd_rel->relkind == RELKIND_RELATION)
11150 {
11151 /*
11152 * Tell Phase 3 to check that the constraint is satisfied by existing
11153 * rows. We can skip this during table creation, when constraint is
11154 * specified as NOT ENFORCED, or when requested explicitly by
11155 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11156 * recreating a constraint following a SET DATA TYPE operation that
11157 * did not impugn its validity.
11158 */
11159 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11160 fkconstraint->is_enforced)
11161 {
11163 AlteredTableInfo *tab;
11164
11165 tab = ATGetQueueEntry(wqueue, rel);
11166
11169 newcon->contype = CONSTR_FOREIGN;
11170 newcon->refrelid = RelationGetRelid(pkrel);
11171 newcon->refindid = indexOid;
11172 newcon->conid = parentConstr;
11173 newcon->conwithperiod = fkconstraint->fk_with_period;
11174 newcon->qual = (Node *) fkconstraint;
11175
11176 tab->constraints = lappend(tab->constraints, newcon);
11177 }
11178 }
11179 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11180 {
11183
11184 /*
11185 * Triggers of the foreign keys will be manipulated a bunch of times
11186 * in the loop below. To avoid repeatedly opening/closing the trigger
11187 * catalog relation, we open it here and pass it to the subroutines
11188 * called below.
11189 */
11191
11192 /*
11193 * Recurse to take appropriate action on each partition; either we
11194 * find an existing constraint to reparent to ours, or we create a new
11195 * one.
11196 */
11197 for (int i = 0; i < pd->nparts; i++)
11198 {
11199 Relation partition = table_open(pd->oids[i], lockmode);
11200 List *partFKs;
11201 AttrMap *attmap;
11203 bool attached;
11204 ObjectAddress address;
11205
11207
11209 RelationGetDescr(rel),
11210 false);
11211 for (int j = 0; j < numfks; j++)
11212 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11213
11214 /* Check whether an existing constraint can be repurposed */
11216 attached = false;
11218 {
11220 fk,
11221 partition,
11223 numfks,
11225 pkattnum,
11229 trigrel))
11230 {
11231 attached = true;
11232 break;
11233 }
11234 }
11235 if (attached)
11236 {
11238 continue;
11239 }
11240
11241 /*
11242 * No luck finding a good constraint to reuse; create our own.
11243 */
11245 fkconstraint->conname, fkconstraint,
11246 partition, pkrel, indexOid, parentConstr,
11251 with_period);
11252
11253 /* call ourselves to finalize the creation and we're done */
11255 indexOid,
11256 address.objectId,
11257 numfks,
11258 pkattnum,
11266 lockmode,
11269 with_period);
11270
11272 }
11273
11275 }
11276}
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:1227
#define copyObject(obj)
Definition nodes.h:232
@ CONSTR_FOREIGN
#define INDEX_MAX_KEYS
#define foreach_node(type, var, lst)
Definition pg_list.h:496
List * RelationGetFKeyList(Relation relation)
Definition relcache.c:4721
Definition pg_list.h:54
Definition nodes.h:135
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition tablecmds.c:6620
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:4480
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 6654 of file tablecmds.c.

6655{
6656 switch (cmdtype)
6657 {
6658 case AT_AddColumn:
6659 case AT_AddColumnToView:
6660 return "ADD COLUMN";
6661 case AT_ColumnDefault:
6663 return "ALTER COLUMN ... SET DEFAULT";
6664 case AT_DropNotNull:
6665 return "ALTER COLUMN ... DROP NOT NULL";
6666 case AT_SetNotNull:
6667 return "ALTER COLUMN ... SET NOT NULL";
6668 case AT_SetExpression:
6669 return "ALTER COLUMN ... SET EXPRESSION";
6670 case AT_DropExpression:
6671 return "ALTER COLUMN ... DROP EXPRESSION";
6672 case AT_SetStatistics:
6673 return "ALTER COLUMN ... SET STATISTICS";
6674 case AT_SetOptions:
6675 return "ALTER COLUMN ... SET";
6676 case AT_ResetOptions:
6677 return "ALTER COLUMN ... RESET";
6678 case AT_SetStorage:
6679 return "ALTER COLUMN ... SET STORAGE";
6680 case AT_SetCompression:
6681 return "ALTER COLUMN ... SET COMPRESSION";
6682 case AT_DropColumn:
6683 return "DROP COLUMN";
6684 case AT_AddIndex:
6685 case AT_ReAddIndex:
6686 return NULL; /* not real grammar */
6687 case AT_AddConstraint:
6688 case AT_ReAddConstraint:
6691 return "ADD CONSTRAINT";
6692 case AT_AlterConstraint:
6693 return "ALTER CONSTRAINT";
6695 return "VALIDATE CONSTRAINT";
6696 case AT_DropConstraint:
6697 return "DROP CONSTRAINT";
6698 case AT_ReAddComment:
6699 return NULL; /* not real grammar */
6700 case AT_AlterColumnType:
6701 return "ALTER COLUMN ... SET DATA TYPE";
6703 return "ALTER COLUMN ... OPTIONS";
6704 case AT_ChangeOwner:
6705 return "OWNER TO";
6706 case AT_ClusterOn:
6707 return "CLUSTER ON";
6708 case AT_DropCluster:
6709 return "SET WITHOUT CLUSTER";
6710 case AT_SetAccessMethod:
6711 return "SET ACCESS METHOD";
6712 case AT_SetLogged:
6713 return "SET LOGGED";
6714 case AT_SetUnLogged:
6715 return "SET UNLOGGED";
6716 case AT_DropOids:
6717 return "SET WITHOUT OIDS";
6718 case AT_SetTableSpace:
6719 return "SET TABLESPACE";
6720 case AT_SetRelOptions:
6721 return "SET";
6722 case AT_ResetRelOptions:
6723 return "RESET";
6725 return NULL; /* not real grammar */
6726 case AT_EnableTrig:
6727 return "ENABLE TRIGGER";
6729 return "ENABLE ALWAYS TRIGGER";
6731 return "ENABLE REPLICA TRIGGER";
6732 case AT_DisableTrig:
6733 return "DISABLE TRIGGER";
6734 case AT_EnableTrigAll:
6735 return "ENABLE TRIGGER ALL";
6736 case AT_DisableTrigAll:
6737 return "DISABLE TRIGGER ALL";
6738 case AT_EnableTrigUser:
6739 return "ENABLE TRIGGER USER";
6740 case AT_DisableTrigUser:
6741 return "DISABLE TRIGGER USER";
6742 case AT_EnableRule:
6743 return "ENABLE RULE";
6745 return "ENABLE ALWAYS RULE";
6747 return "ENABLE REPLICA RULE";
6748 case AT_DisableRule:
6749 return "DISABLE RULE";
6750 case AT_AddInherit:
6751 return "INHERIT";
6752 case AT_DropInherit:
6753 return "NO INHERIT";
6754 case AT_AddOf:
6755 return "OF";
6756 case AT_DropOf:
6757 return "NOT OF";
6758 case AT_ReplicaIdentity:
6759 return "REPLICA IDENTITY";
6761 return "ENABLE ROW SECURITY";
6763 return "DISABLE ROW SECURITY";
6765 return "FORCE ROW SECURITY";
6767 return "NO FORCE ROW SECURITY";
6768 case AT_GenericOptions:
6769 return "OPTIONS";
6770 case AT_AttachPartition:
6771 return "ATTACH PARTITION";
6772 case AT_DetachPartition:
6773 return "DETACH PARTITION";
6775 return "DETACH PARTITION ... FINALIZE";
6776 case AT_MergePartitions:
6777 return "MERGE PARTITIONS";
6778 case AT_SplitPartition:
6779 return "SPLIT PARTITION";
6780 case AT_AddIdentity:
6781 return "ALTER COLUMN ... ADD IDENTITY";
6782 case AT_SetIdentity:
6783 return "ALTER COLUMN ... SET";
6784 case AT_DropIdentity:
6785 return "ALTER COLUMN ... DROP IDENTITY";
6786 case AT_ReAddStatistics:
6787 return NULL; /* not real grammar */
6788 }
6789
6790 return NULL;
6791}
@ 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 12752 of file tablecmds.c.

12756{
12759 ScanKeyData skey[3];
12760
12761 ScanKeyInit(&skey[0],
12764 ObjectIdGetDatum(conrelid));
12765 ScanKeyInit(&skey[1],
12769 ScanKeyInit(&skey[2],
12772 CStringGetDatum(cmdcon->conname));
12773
12775 NULL, 3, skey);
12776
12778 ereport(ERROR,
12780 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12781 cmdcon->conname, get_rel_name(conrelid)));
12782
12784 recurse, recursing, lockmode);
12785
12787}
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:603
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:514
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
#define HeapTupleIsValid(tuple)
Definition htup.h:78
char * get_rel_name(Oid relid)
Definition lsyscache.c:2148
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
static Datum CStringGetDatum(const char *X)
Definition postgres.h:370
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 13054 of file tablecmds.c.

13058{
13060 Oid conoid;
13061 ScanKeyData pkey;
13064
13066 conoid = currcon->oid;
13067
13068 ScanKeyInit(&pkey,
13071 ObjectIdGetDatum(conoid));
13072
13074 true, NULL, 1, &pkey);
13075
13077 {
13080
13081 childrel = table_open(childcon->conrelid, lockmode);
13082
13084 childtup, recurse, otherrelids, lockmode);
13086 }
13087
13089}
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 12936 of file tablecmds.c.

12939{
12943
12947 ObjectIdGetDatum(conoid));
12949 NULL, 1, &tgkey);
12951 {
12955
12956 /*
12957 * Remember OIDs of other relation(s) involved in FK constraint.
12958 * (Note: it's likely that we could skip forcing a relcache inval for
12959 * other rels that don't have a trigger whose properties change, but
12960 * let's be conservative.)
12961 */
12962 if (tgform->tgrelid != RelationGetRelid(rel))
12964 tgform->tgrelid);
12965
12966 /*
12967 * Update enable status and deferrability of RI_FKey_noaction_del,
12968 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12969 * triggers, but not others; see createForeignKeyActionTriggers and
12970 * CreateFKCheckTrigger.
12971 */
12972 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12973 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12974 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12975 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12976 continue;
12977
12980
12981 copy_tg->tgdeferrable = deferrable;
12982 copy_tg->tginitdeferred = initdeferred;
12984
12986
12988 }
12989
12991}
HeapTuple heap_copytuple(HeapTuple tuple)
Definition heaptuple.c:698
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1384
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 13096 of file tablecmds.c.

13098{
13101
13102 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
13103 cmdcon->alterInheritability);
13104
13107
13108 if (cmdcon->alterEnforceability)
13109 {
13110 copy_con->conenforced = cmdcon->is_enforced;
13111
13112 /*
13113 * NB: The convalidated status is irrelevant when the constraint is
13114 * set to NOT ENFORCED, but for consistency, it should still be set
13115 * appropriately. Similarly, if the constraint is later changed to
13116 * ENFORCED, validation will be performed during phase 3, so it makes
13117 * sense to mark it as valid in that case.
13118 */
13119 copy_con->convalidated = cmdcon->is_enforced;
13120 }
13121 if (cmdcon->alterDeferrability)
13122 {
13123 copy_con->condeferrable = cmdcon->deferrable;
13124 copy_con->condeferred = cmdcon->initdeferred;
13125 }
13126 if (cmdcon->alterInheritability)
13127 copy_con->connoinherit = cmdcon->noinherit;
13128
13131
13132 /* Make new constraint flags visible to others */
13134
13136}
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 13005 of file tablecmds.c.

13013{
13015 Oid conoid;
13016 ScanKeyData pkey;
13019
13021 conoid = currcon->oid;
13022
13023 ScanKeyInit(&pkey,
13026 ObjectIdGetDatum(conoid));
13027
13029 true, NULL, 1, &pkey);
13030
13033 pkrelid, childtup, lockmode,
13038
13040}
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 19365 of file tablecmds.c.

19367{
19368 List *indexList;
19369 ListCell *l;
19370
19372
19373 foreach(l, indexList)
19374 {
19375 Oid indexOid = lfirst_oid(l);
19377
19379 thisobj.objectId = indexOid;
19380 thisobj.objectSubId = 0;
19381
19382 /*
19383 * Note: currently, the index will not have its own dependency on the
19384 * namespace, so we don't need to do changeDependencyFor(). There's no
19385 * row type in pg_type, either.
19386 *
19387 * XXX this objsMoved test may be pointless -- surely we have a single
19388 * dependency link from a relation to each index?
19389 */
19391 {
19394 false, objsMoved);
19396 }
19397 }
19398
19400}
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:4826
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 19288 of file tablecmds.c.

19292{
19296 bool already_done = false;
19297
19298 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19301 elog(ERROR, "cache lookup failed for relation %u", relOid);
19303
19304 Assert(classForm->relnamespace == oldNspOid);
19305
19306 thisobj.classId = RelationRelationId;
19307 thisobj.objectId = relOid;
19308 thisobj.objectSubId = 0;
19309
19310 /*
19311 * If the object has already been moved, don't move it again. If it's
19312 * already in the right place, don't move it, but still fire the object
19313 * access hook.
19314 */
19316 if (!already_done && oldNspOid != newNspOid)
19317 {
19318 ItemPointerData otid = classTup->t_self;
19319
19320 /* check for duplicate name (more friendly than unique-index failure) */
19321 if (get_relname_relid(NameStr(classForm->relname),
19323 ereport(ERROR,
19325 errmsg("relation \"%s\" already exists in schema \"%s\"",
19326 NameStr(classForm->relname),
19328
19329 /* classTup is a copy, so OK to scribble on */
19330 classForm->relnamespace = newNspOid;
19331
19334
19335
19336 /* Update dependency on schema if caller said so */
19337 if (hasDependEntry &&
19339 relOid,
19341 oldNspOid,
19342 newNspOid) != 1)
19343 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19344 NameStr(classForm->relname));
19345 }
19346 else
19348 if (!already_done)
19349 {
19351
19353 }
19354
19356}
#define NameStr(name)
Definition c.h:837
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:3588
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition lsyscache.c:2105
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition pg_depend.c:459
HeapTuple SearchSysCacheLockedCopy1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:399

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

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

◆ AlterSeqNamespaces()

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

Definition at line 19410 of file tablecmds.c.

19413{
19415 SysScanDesc scan;
19416 ScanKeyData key[2];
19417 HeapTuple tup;
19418
19419 /*
19420 * SERIAL sequences are those having an auto dependency on one of the
19421 * table's columns (we don't care *which* column, exactly).
19422 */
19424
19425 ScanKeyInit(&key[0],
19429 ScanKeyInit(&key[1],
19433 /* we leave refobjsubid unspecified */
19434
19436 NULL, 2, key);
19437
19438 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19439 {
19442
19443 /* skip dependencies other than auto dependencies on columns */
19444 if (depForm->refobjsubid == 0 ||
19445 depForm->classid != RelationRelationId ||
19446 depForm->objsubid != 0 ||
19447 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19448 continue;
19449
19450 /* Use relation_open just in case it's an index */
19451 seqRel = relation_open(depForm->objid, lockmode);
19452
19453 /* skip non-sequence relations */
19454 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19455 {
19456 /* No need to keep the lock */
19457 relation_close(seqRel, lockmode);
19458 continue;
19459 }
19460
19461 /* Fix the pg_class and pg_depend entries */
19464 true, objsMoved);
19465
19466 /*
19467 * Sequences used to have entries in pg_type, but no longer do. If we
19468 * ever re-instate that, we'll need to move the pg_type entry to the
19469 * new namespace, too (using AlterTypeNamespaceInternal).
19470 */
19472
19473 /* Now we can close it. Keep the lock till end of transaction. */
19475 }
19476
19477 systable_endscan(scan);
19478
19480}
@ DEPENDENCY_AUTO
Definition dependency.h:34
#define AccessShareLock
Definition lockdefs.h:36
END_CATALOG_STRUCT typedef FormData_pg_depend * Form_pg_depend
Definition pg_depend.h:76
#define RelationGetForm(relation)
Definition rel.h:508
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:205
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:47

References AccessShareLock, AlterRelationNamespaceInternal(), Assert, BTEqualStrategyNumber, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, fb(), Form_pg_depend, GETSTRUCT(), HeapTupleIsValid, InvalidOid, NoLock, ObjectIdGetDatum(), relation_close(), relation_open(), RelationGetForm, RelationGetRelid, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), and table_open().

Referenced by AlterTableNamespaceInternal().

◆ AlterTable()

void AlterTable ( AlterTableStmt stmt,
LOCKMODE  lockmode,
AlterTableUtilityContext context 
)

Definition at line 4565 of file tablecmds.c.

4567{
4568 Relation rel;
4569
4570 /* Caller is required to provide an adequate lock. */
4571 rel = relation_open(context->relid, NoLock);
4572
4574
4575 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4576}
#define stmt
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:4906

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4639 of file tablecmds.c.

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

4595{
4596 Relation rel;
4597 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4598
4599 rel = relation_open(relid, lockmode);
4600
4602
4603 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4604}
void EventTriggerAlterTableRelid(Oid objectId)
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition tablecmds.c:4639

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4506 of file tablecmds.c.

4507{
4508 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4509 stmt->missing_ok ? RVR_MISSING_OK : 0,
4511 stmt);
4512}
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 17228 of file tablecmds.c.

17229{
17230 List *relations = NIL;
17231 ListCell *l;
17232 ScanKeyData key[1];
17233 Relation rel;
17234 TableScanDesc scan;
17235 HeapTuple tuple;
17238 List *role_oids = roleSpecsToIds(stmt->roles);
17239
17240 /* Ensure we were not asked to move something we can't */
17241 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17242 stmt->objtype != OBJECT_MATVIEW)
17243 ereport(ERROR,
17245 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17246
17247 /* Get the orig and new tablespace OIDs */
17248 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17249 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17250
17251 /* Can't move shared relations in to or out of pg_global */
17252 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17255 ereport(ERROR,
17257 errmsg("cannot move relations in to or out of pg_global tablespace")));
17258
17259 /*
17260 * Must have CREATE rights on the new tablespace, unless it is the
17261 * database default tablespace (which all users implicitly have CREATE
17262 * rights on).
17263 */
17265 {
17267
17269 ACL_CREATE);
17270 if (aclresult != ACLCHECK_OK)
17273 }
17274
17275 /*
17276 * Now that the checks are done, check if we should set either to
17277 * InvalidOid because it is our database's default tablespace.
17278 */
17281
17284
17285 /* no-op */
17287 return new_tablespaceoid;
17288
17289 /*
17290 * Walk the list of objects in the tablespace and move them. This will
17291 * only find objects in our database, of course.
17292 */
17293 ScanKeyInit(&key[0],
17297
17299 scan = table_beginscan_catalog(rel, 1, key);
17300 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17301 {
17303 Oid relOid = relForm->oid;
17304
17305 /*
17306 * Do not move objects in pg_catalog as part of this, if an admin
17307 * really wishes to do so, they can issue the individual ALTER
17308 * commands directly.
17309 *
17310 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17311 * (TOAST will be moved with the main table).
17312 */
17313 if (IsCatalogNamespace(relForm->relnamespace) ||
17314 relForm->relisshared ||
17315 isAnyTempNamespace(relForm->relnamespace) ||
17316 IsToastNamespace(relForm->relnamespace))
17317 continue;
17318
17319 /* Only move the object type requested */
17320 if ((stmt->objtype == OBJECT_TABLE &&
17321 relForm->relkind != RELKIND_RELATION &&
17322 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17323 (stmt->objtype == OBJECT_INDEX &&
17324 relForm->relkind != RELKIND_INDEX &&
17325 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17326 (stmt->objtype == OBJECT_MATVIEW &&
17327 relForm->relkind != RELKIND_MATVIEW))
17328 continue;
17329
17330 /* Check if we are only moving objects owned by certain roles */
17331 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17332 continue;
17333
17334 /*
17335 * Handle permissions-checking here since we are locking the tables
17336 * and also to avoid doing a bunch of work only to fail part-way. Note
17337 * that permissions will also be checked by AlterTableInternal().
17338 *
17339 * Caller must be considered an owner on the table to move it.
17340 */
17343 NameStr(relForm->relname));
17344
17345 if (stmt->nowait &&
17347 ereport(ERROR,
17349 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17350 get_namespace_name(relForm->relnamespace),
17351 NameStr(relForm->relname))));
17352 else
17354
17355 /* Add to our list of objects to move */
17356 relations = lappend_oid(relations, relOid);
17357 }
17358
17359 table_endscan(scan);
17361
17362 if (relations == NIL)
17365 errmsg("no matching relations in tablespace \"%s\" found",
17366 orig_tablespaceoid == InvalidOid ? "(database default)" :
17368
17369 /* Everything is locked, loop through and move all of the relations. */
17370 foreach(l, relations)
17371 {
17372 List *cmds = NIL;
17374
17376 cmd->name = stmt->new_tablespacename;
17377
17378 cmds = lappend(cmds, cmd);
17379
17381 /* OID is set by AlterTableInternal */
17382 AlterTableInternal(lfirst_oid(l), cmds, false);
17384 }
17385
17386 return new_tablespaceoid;
17387}
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:35
void EventTriggerAlterTableStart(const Node *parsetree)
void EventTriggerAlterTableEnd(void)
Oid MyDatabaseTableSpace
Definition globals.c:96
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition heapam.c:1420
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:2223
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:1004
void AlterTableInternal(Oid relid, List *cmds, bool recurse)
Definition tablecmds.c:4594
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 19180 of file tablecmds.c.

19181{
19182 Relation rel;
19183 Oid relid;
19184 Oid oldNspOid;
19185 Oid nspOid;
19186 RangeVar *newrv;
19189
19191 stmt->missing_ok ? RVR_MISSING_OK : 0,
19193 stmt);
19194
19195 if (!OidIsValid(relid))
19196 {
19198 (errmsg("relation \"%s\" does not exist, skipping",
19199 stmt->relation->relname)));
19200 return InvalidObjectAddress;
19201 }
19202
19203 rel = relation_open(relid, NoLock);
19204
19206
19207 /* If it's an owned sequence, disallow moving it by itself. */
19208 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19209 {
19210 Oid tableId;
19211 int32 colId;
19212
19213 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19215 ereport(ERROR,
19217 errmsg("cannot move an owned sequence into another schema"),
19218 errdetail("Sequence \"%s\" is linked to table \"%s\".",
19221 }
19222
19223 /* Get and lock schema OID and check its permissions. */
19224 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19226
19227 /* common checks on switching namespaces */
19229
19233
19235
19236 if (oldschema)
19238
19239 /* close rel, but keep lock until commit */
19240 relation_close(rel, NoLock);
19241
19242 return myself;
19243}
int32_t int32
Definition c.h:614
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 19251 of file tablecmds.c.

19253{
19255
19256 Assert(objsMoved != NULL);
19257
19258 /* OK, modify the pg_class row and pg_depend entry */
19260
19262 nspOid, true, objsMoved);
19263
19264 /* Fix the table's row type too, if it has one */
19265 if (OidIsValid(rel->rd_rel->reltype))
19267 false, /* isImplicitArray */
19268 false, /* ignoreDependent */
19269 false, /* errorOnTableType */
19270 objsMoved);
19271
19272 /* Fix other dependent stuff */
19277 false, objsMoved);
19278
19280}
void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, Oid newNspId, bool isType, ObjectAddresses *objsMoved)
static void AlterSeqNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition typecmds.c:4197

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

Referenced by AlterObjectNamespace_oid(), and AlterTableNamespace().

◆ ATAddCheckNNConstraint()

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

Definition at line 9987 of file tablecmds.c.

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

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

10145{
10158 bool with_period;
10160 int i;
10161 int numfks,
10162 numpks,
10164 Oid indexOid;
10165 bool old_check_ok;
10166 ObjectAddress address;
10168
10169 /*
10170 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10171 * delete rows out from under us.
10172 */
10173 if (OidIsValid(fkconstraint->old_pktable_oid))
10175 else
10177
10178 /*
10179 * Validity checks (permission checks wait till we have the column
10180 * numbers)
10181 */
10182 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10183 ereport(ERROR,
10185 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10188
10189 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10190 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10191 ereport(ERROR,
10193 errmsg("referenced relation \"%s\" is not a table",
10195
10197 ereport(ERROR,
10199 errmsg("permission denied: \"%s\" is a system catalog",
10201
10202 /*
10203 * References from permanent or unlogged tables to temp tables, and from
10204 * permanent tables to unlogged tables, are disallowed because the
10205 * referenced data can vanish out from under us. References from temp
10206 * tables to any other table type are also disallowed, because other
10207 * backends might need to run the RI triggers on the perm table, but they
10208 * can't reliably see tuples in the local buffers of other backends.
10209 */
10210 switch (rel->rd_rel->relpersistence)
10211 {
10214 ereport(ERROR,
10216 errmsg("constraints on permanent tables may reference only permanent tables")));
10217 break;
10220 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10221 ereport(ERROR,
10223 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10224 break;
10226 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10227 ereport(ERROR,
10229 errmsg("constraints on temporary tables may reference only temporary tables")));
10230 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10231 ereport(ERROR,
10233 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10234 break;
10235 }
10236
10237 /*
10238 * Look up the referencing attributes to make sure they exist, and record
10239 * their attnums and type and collation OIDs.
10240 */
10242 fkconstraint->fk_attrs,
10244 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10245 if (with_period && !fkconstraint->fk_with_period)
10246 ereport(ERROR,
10248 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10249
10251 fkconstraint->fk_del_set_cols,
10256 fkconstraint->fk_del_set_cols);
10257
10258 /*
10259 * If the attribute list for the referenced table was omitted, lookup the
10260 * definition of the primary key and use it. Otherwise, validate the
10261 * supplied attribute list. In either case, discover the index OID and
10262 * index opclasses, and the attnums and type and collation OIDs of the
10263 * attributes.
10264 */
10265 if (fkconstraint->pk_attrs == NIL)
10266 {
10268 &fkconstraint->pk_attrs,
10271
10272 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10273 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10274 ereport(ERROR,
10276 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10277 }
10278 else
10279 {
10281 fkconstraint->pk_attrs,
10283
10284 /* Since we got pk_attrs, one should be a period. */
10285 if (with_period && !fkconstraint->pk_with_period)
10286 ereport(ERROR,
10288 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10289
10290 /* Look for an index matching the column list */
10293 }
10294
10295 /*
10296 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10297 * must use PERIOD.
10298 */
10300 ereport(ERROR,
10302 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10303
10304 /*
10305 * Now we can check permissions.
10306 */
10308
10309 /*
10310 * Check some things for generated columns.
10311 */
10312 for (i = 0; i < numfks; i++)
10313 {
10314 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10315
10316 if (attgenerated)
10317 {
10318 /*
10319 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10320 */
10321 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10322 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10323 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10324 ereport(ERROR,
10326 errmsg("invalid %s action for foreign key constraint containing generated column",
10327 "ON UPDATE")));
10328 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10329 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10330 ereport(ERROR,
10332 errmsg("invalid %s action for foreign key constraint containing generated column",
10333 "ON DELETE")));
10334 }
10335
10336 /*
10337 * FKs on virtual columns are not supported. This would require
10338 * various additional support in ri_triggers.c, including special
10339 * handling in ri_NullCheck(), ri_KeysEqual(),
10340 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10341 * as NULL there). Also not really practical as long as you can't
10342 * index virtual columns.
10343 */
10344 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10345 ereport(ERROR,
10347 errmsg("foreign key constraints on virtual generated columns are not supported")));
10348 }
10349
10350 /*
10351 * Some actions are currently unsupported for foreign keys using PERIOD.
10352 */
10353 if (fkconstraint->fk_with_period)
10354 {
10355 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10356 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10357 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10358 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10359 ereport(ERROR,
10361 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10362 "ON UPDATE"));
10363
10364 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10365 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10366 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10367 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10368 ereport(ERROR,
10370 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10371 "ON DELETE"));
10372 }
10373
10374 /*
10375 * Look up the equality operators to use in the constraint.
10376 *
10377 * Note that we have to be careful about the difference between the actual
10378 * PK column type and the opclass' declared input type, which might be
10379 * only binary-compatible with it. The declared opcintype is the right
10380 * thing to probe pg_amop with.
10381 */
10382 if (numfks != numpks)
10383 ereport(ERROR,
10385 errmsg("number of referencing and referenced columns for foreign key disagree")));
10386
10387 /*
10388 * On the strength of a previous constraint, we might avoid scanning
10389 * tables to validate this one. See below.
10390 */
10391 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10392 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10393
10394 for (i = 0; i < numpks; i++)
10395 {
10396 Oid pktype = pktypoid[i];
10397 Oid fktype = fktypoid[i];
10398 Oid fktyped;
10399 Oid pkcoll = pkcolloid[i];
10400 Oid fkcoll = fkcolloid[i];
10403 Oid amid;
10404 Oid opfamily;
10405 Oid opcintype;
10406 bool for_overlaps;
10407 CompareType cmptype;
10408 Oid pfeqop;
10409 Oid ppeqop;
10410 Oid ffeqop;
10413
10414 /* We need several fields out of the pg_opclass entry */
10417 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10419 amid = cla_tup->opcmethod;
10420 opfamily = cla_tup->opcfamily;
10421 opcintype = cla_tup->opcintype;
10423
10424 /*
10425 * Get strategy number from index AM.
10426 *
10427 * For a normal foreign-key constraint, this should not fail, since we
10428 * already checked that the index is unique and should therefore have
10429 * appropriate equal operators. For a period foreign key, this could
10430 * fail if we selected a non-matching exclusion constraint earlier.
10431 * (XXX Maybe we should do these lookups earlier so we don't end up
10432 * doing that.)
10433 */
10434 for_overlaps = with_period && i == numpks - 1;
10436 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10438 ereport(ERROR,
10441 ? errmsg("could not identify an overlaps operator for foreign key")
10442 : errmsg("could not identify an equality operator for foreign key"),
10443 errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10444 cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10445
10446 /*
10447 * There had better be a primary equality operator for the index.
10448 * We'll use it for PK = PK comparisons.
10449 */
10450 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10451 eqstrategy);
10452
10453 if (!OidIsValid(ppeqop))
10454 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10455 eqstrategy, opcintype, opcintype, opfamily);
10456
10457 /*
10458 * Are there equality operators that take exactly the FK type? Assume
10459 * we should look through any domain here.
10460 */
10462
10463 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10464 eqstrategy);
10465 if (OidIsValid(pfeqop))
10466 {
10469 eqstrategy);
10470 }
10471 else
10472 {
10473 /* keep compiler quiet */
10476 }
10477
10478 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10479 {
10480 /*
10481 * Otherwise, look for an implicit cast from the FK type to the
10482 * opcintype, and if found, use the primary equality operator.
10483 * This is a bit tricky because opcintype might be a polymorphic
10484 * type such as ANYARRAY or ANYENUM; so what we have to test is
10485 * whether the two actual column types can be concurrently cast to
10486 * that type. (Otherwise, we'd fail to reject combinations such
10487 * as int[] and point[].)
10488 */
10489 Oid input_typeids[2];
10491
10492 input_typeids[0] = pktype;
10493 input_typeids[1] = fktype;
10494 target_typeids[0] = opcintype;
10495 target_typeids[1] = opcintype;
10498 {
10499 pfeqop = ffeqop = ppeqop;
10500 pfeqop_right = opcintype;
10501 }
10502 }
10503
10504 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10505 ereport(ERROR,
10507 errmsg("foreign key constraint \"%s\" cannot be implemented",
10508 fkconstraint->conname),
10509 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10510 "are of incompatible types: %s and %s.",
10511 strVal(list_nth(fkconstraint->fk_attrs, i)),
10512 strVal(list_nth(fkconstraint->pk_attrs, i)),
10515
10516 /*
10517 * This shouldn't be possible, but better check to make sure we have a
10518 * consistent state for the check below.
10519 */
10521 elog(ERROR, "key columns are not both collatable");
10522
10524 {
10525 bool pkcolldet;
10526 bool fkcolldet;
10527
10530
10531 /*
10532 * SQL requires that both collations are the same. This is
10533 * because we need a consistent notion of equality on both
10534 * columns. We relax this by allowing different collations if
10535 * they are both deterministic. (This is also for backward
10536 * compatibility, because PostgreSQL has always allowed this.)
10537 */
10538 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10539 ereport(ERROR,
10541 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10542 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10543 "have incompatible collations: \"%s\" and \"%s\". "
10544 "If either collation is nondeterministic, then both collations have to be the same.",
10545 strVal(list_nth(fkconstraint->fk_attrs, i)),
10546 strVal(list_nth(fkconstraint->pk_attrs, i)),
10549 }
10550
10551 if (old_check_ok)
10552 {
10553 /*
10554 * When a pfeqop changes, revalidate the constraint. We could
10555 * permit intra-opfamily changes, but that adds subtle complexity
10556 * without any concrete benefit for core types. We need not
10557 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10558 */
10560 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10562 }
10563 if (old_check_ok)
10564 {
10574 fkattnum[i] - 1);
10575
10576 /*
10577 * Identify coercion pathways from each of the old and new FK-side
10578 * column types to the right (foreign) operand type of the pfeqop.
10579 * We may assume that pg_constraint.conkey is not changing.
10580 */
10581 old_fktype = attr->atttypid;
10584 &old_castfunc);
10586 &new_castfunc);
10587
10588 old_fkcoll = attr->attcollation;
10590
10591 /*
10592 * Upon a change to the cast from the FK column to its pfeqop
10593 * operand, revalidate the constraint. For this evaluation, a
10594 * binary coercion cast is equivalent to no cast at all. While
10595 * type implementors should design implicit casts with an eye
10596 * toward consistency of operations like equality, we cannot
10597 * assume here that they have done so.
10598 *
10599 * A function with a polymorphic argument could change behavior
10600 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10601 * when the cast destination is polymorphic, we only avoid
10602 * revalidation if the input type has not changed at all. Given
10603 * just the core data types and operator classes, this requirement
10604 * prevents no would-be optimizations.
10605 *
10606 * If the cast converts from a base type to a domain thereon, then
10607 * that domain type must be the opcintype of the unique index.
10608 * Necessarily, the primary key column must then be of the domain
10609 * type. Since the constraint was previously valid, all values on
10610 * the foreign side necessarily exist on the primary side and in
10611 * turn conform to the domain. Consequently, we need not treat
10612 * domains specially here.
10613 *
10614 * If the collation changes, revalidation is required, unless both
10615 * collations are deterministic, because those share the same
10616 * notion of equality (because texteq reduces to bitwise
10617 * equality).
10618 *
10619 * We need not directly consider the PK type. It's necessarily
10620 * binary coercible to the opcintype of the unique index column,
10621 * and ri_triggers.c will only deal with PK datums in terms of
10622 * that opcintype. Changing the opcintype also changes pfeqop.
10623 */
10627 new_fktype == old_fktype) &&
10628 (new_fkcoll == old_fkcoll ||
10630 }
10631
10635 }
10636
10637 /*
10638 * For FKs with PERIOD we need additional operators to check whether the
10639 * referencing row's range is contained by the aggregated ranges of the
10640 * referenced row(s). For rangetypes and multirangetypes this is
10641 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10642 * support for now. FKs will look these up at "runtime", but we should
10643 * make sure the lookup works here, even if we don't use the values.
10644 */
10645 if (with_period)
10646 {
10650
10653 }
10654
10655 /* First, create the constraint catalog entry itself. */
10657 fkconstraint->conname, fkconstraint, rel, pkrel,
10658 indexOid,
10659 InvalidOid, /* no parent constraint */
10660 numfks,
10661 pkattnum,
10662 fkattnum,
10668 false,
10669 with_period);
10670
10671 /* Next process the action triggers at the referenced side and recurse */
10673 indexOid,
10674 address.objectId,
10675 numfks,
10676 pkattnum,
10677 fkattnum,
10685 with_period);
10686
10687 /* Lastly create the check triggers at the referencing side and recurse */
10689 indexOid,
10690 address.objectId,
10691 numfks,
10692 pkattnum,
10693 fkattnum,
10700 lockmode,
10702 with_period);
10703
10704 /*
10705 * Done. Close pk table, but keep lock until we've committed.
10706 */
10708
10709 return address;
10710}
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition amapi.c:161
char * get_am_name(Oid amOid)
Definition amcmds.c:192
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
CompareType
Definition cmptype.h:32
@ COMPARE_OVERLAP
Definition cmptype.h:40
@ COMPARE_EQ
Definition cmptype.h:36
char * format_type_be(Oid type_oid)
bool allowSystemTableMods
Definition globals.c:130
#define false
char * get_collation_name(Oid colloid)
Definition lsyscache.c:1181
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:1200
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition lsyscache.c:1473
Oid getBaseType(Oid typid)
Definition lsyscache.c:2743
bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids, CoercionContext ccontext)
CoercionPathType
#define FKCONSTR_ACTION_RESTRICT
#define FKCONSTR_ACTION_SETDEFAULT
#define FKCONSTR_ACTION_CASCADE
#define FKCONSTR_ACTION_SETNULL
FormData_pg_attribute * Form_pg_attribute
void FindFKPeriodOpers(Oid opclass, Oid *containedbyoperoid, Oid *aggedcontainedbyoperoid, Oid *intersectoperoid)
static void * list_nth(const List *list, int n)
Definition pg_list.h:299
static ListCell * list_head(const List *l)
Definition pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition pg_list.h:343
END_CATALOG_STRUCT typedef FormData_pg_opclass * Form_pg_opclass
Definition pg_opclass.h:87
@ COERCION_IMPLICIT
Definition primnodes.h:747
#define RelationIsPermanent(relation)
Definition rel.h:626
#define InvalidStrategy
Definition stratnum.h:24
TupleDesc oldDesc
Definition tablecmds.c:175
bool rd_islocaltemp
Definition rel.h:61
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:264
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:220
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition table.c:83
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, List **attnamelist, int16 *attnums, Oid *atttypids, Oid *attcollids, Oid *opclasses, bool *pk_has_without_overlaps)
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h: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 6923 of file tablecmds.c.

6924{
6925 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6926 {
6927 List *inh;
6928 ListCell *cell;
6929
6930 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6931 /* first element is the parent rel; must ignore it */
6932 for_each_from(cell, inh, 1)
6933 {
6935
6936 /* find_all_inheritors already got lock */
6940 }
6941 list_free(inh);
6942 }
6943}
List * find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
#define for_each_from(cell, lst, N)
Definition pg_list.h:414

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

Referenced by ATPrepCmd().

◆ ATColumnChangeRequiresRewrite()

static bool ATColumnChangeRequiresRewrite ( Node expr,
AttrNumber  varattno 
)
static

Definition at line 14921 of file tablecmds.c.

14922{
14923 Assert(expr != NULL);
14924
14925 for (;;)
14926 {
14927 /* only one varno, so no need to check that */
14928 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14929 return false;
14930 else if (IsA(expr, RelabelType))
14931 expr = (Node *) ((RelabelType *) expr)->arg;
14932 else if (IsA(expr, CoerceToDomain))
14933 {
14934 CoerceToDomain *d = (CoerceToDomain *) expr;
14935
14937 return true;
14938 expr = (Node *) d->arg;
14939 }
14940 else if (IsA(expr, FuncExpr))
14941 {
14942 FuncExpr *f = (FuncExpr *) expr;
14943
14944 switch (f->funcid)
14945 {
14949 return true;
14950 else
14951 expr = linitial(f->args);
14952 break;
14953 default:
14954 return true;
14955 }
14956 }
14957 else
14958 return true;
14959 }
14960}
bool TimestampTimestampTzRequiresRewrite(void)
Definition timestamp.c:6408
Datum arg
Definition elog.c:1322
#define linitial(l)
Definition pg_list.h:178
Oid funcid
Definition primnodes.h:783
List * args
Definition primnodes.h:801
bool DomainHasConstraints(Oid type_id, 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 4906 of file tablecmds.c.

4909{
4910 List *wqueue = NIL;
4911 ListCell *lcmd;
4912
4913 /* Phase 1: preliminary examination of commands, create work queue */
4914 foreach(lcmd, cmds)
4915 {
4917
4918 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4919 }
4920
4921 /* Close the relation, but keep lock until commit */
4922 relation_close(rel, NoLock);
4923
4924 /* Phase 2: update system catalogs */
4925 ATRewriteCatalogs(&wqueue, lockmode, context);
4926
4927 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4928 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4929}
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:5344
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:4941
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:5896

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

22245{
22246 List *constraints;
22247 ListCell *cell;
22248
22249 constraints = GetParentedForeignKeyRefs(partition);
22250
22251 foreach(cell, constraints)
22252 {
22253 Oid constrOid = lfirst_oid(cell);
22254 HeapTuple tuple;
22256 Relation rel;
22257 Trigger trig = {0};
22258
22260 if (!HeapTupleIsValid(tuple))
22261 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22263
22264 Assert(OidIsValid(constrForm->conparentid));
22266
22267 /* prevent data changes into the referencing table until commit */
22268 rel = table_open(constrForm->conrelid, ShareLock);
22269
22270 trig.tgoid = InvalidOid;
22271 trig.tgname = NameStr(constrForm->conname);
22272 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
22273 trig.tgisinternal = true;
22274 trig.tgconstrrelid = RelationGetRelid(partition);
22275 trig.tgconstrindid = constrForm->conindid;
22276 trig.tgconstraint = constrForm->oid;
22277 trig.tgdeferrable = false;
22278 trig.tginitdeferred = false;
22279 /* we needn't fill in remaining fields */
22280
22282
22283 ReleaseSysCache(tuple);
22284
22285 table_close(rel, NoLock);
22286 }
22287}
#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 19693 of file tablecmds.c.

19695{
19697
19698 foreach(cur_item, on_commits)
19699 {
19701
19702 if (!isCommit && oc->creating_subid == mySubid)
19703 {
19704 /* cur_item must be removed */
19706 pfree(oc);
19707 }
19708 else
19709 {
19710 /* cur_item must be preserved */
19711 if (oc->creating_subid == mySubid)
19712 oc->creating_subid = parentSubid;
19713 if (oc->deleting_subid == mySubid)
19714 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19715 }
19716 }
19717}
#define InvalidSubTransactionId
Definition c.h:744
#define foreach_delete_current(lst, var_or_cell)
Definition pg_list.h:391
static List * on_commits
Definition tablecmds.c:133

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

19662{
19664
19665 foreach(cur_item, on_commits)
19666 {
19668
19669 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19670 oc->creating_subid != InvalidSubTransactionId)
19671 {
19672 /* cur_item must be removed */
19674 pfree(oc);
19675 }
19676 else
19677 {
19678 /* cur_item must be preserved */
19679 oc->creating_subid = InvalidSubTransactionId;
19680 oc->deleting_subid = InvalidSubTransactionId;
19681 }
19682 }
19683}

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

7283{
7285 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7286 bool if_not_exists = (*cmd)->missing_ok;
7288 attrdesc;
7291 Form_pg_attribute attribute;
7292 int newattnum;
7293 char relkind;
7294 Expr *defval;
7295 List *children;
7296 ListCell *child;
7298 ObjectAddress address;
7299 TupleDesc tupdesc;
7300
7301 /* since this function recurses, it could be driven to stack overflow */
7303
7304 /* At top level, permission check was done in ATPrepCmd, else do it */
7305 if (recursing)
7306 ATSimplePermissions((*cmd)->subtype, rel,
7308
7309 if (rel->rd_rel->relispartition && !recursing)
7310 ereport(ERROR,
7312 errmsg("cannot add column to a partition")));
7313
7315
7316 /*
7317 * Are we adding the column to a recursion child? If so, check whether to
7318 * merge with an existing definition for the column. If we do merge, we
7319 * must not recurse. Children will already have the column, and recursing
7320 * into them would mess up attinhcount.
7321 */
7322 if (colDef->inhcount > 0)
7323 {
7324 HeapTuple tuple;
7325
7326 /* Does child already have a column by this name? */
7327 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7328 if (HeapTupleIsValid(tuple))
7329 {
7331 Oid ctypeId;
7332 int32 ctypmod;
7333 Oid ccollid;
7334
7335 /* Child column must match on type, typmod, and collation */
7337 if (ctypeId != childatt->atttypid ||
7338 ctypmod != childatt->atttypmod)
7339 ereport(ERROR,
7341 errmsg("child table \"%s\" has different type for column \"%s\"",
7342 RelationGetRelationName(rel), colDef->colname)));
7344 if (ccollid != childatt->attcollation)
7345 ereport(ERROR,
7347 errmsg("child table \"%s\" has different collation for column \"%s\"",
7348 RelationGetRelationName(rel), colDef->colname),
7349 errdetail("\"%s\" versus \"%s\"",
7351 get_collation_name(childatt->attcollation))));
7352
7353 /* Bump the existing child att's inhcount */
7354 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7355 &childatt->attinhcount))
7356 ereport(ERROR,
7358 errmsg("too many inheritance parents"));
7359 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7360
7361 heap_freetuple(tuple);
7362
7363 /* Inform the user about the merge */
7365 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7366 colDef->colname, RelationGetRelationName(rel))));
7367
7369
7370 /* Make the child column change visible */
7372
7373 return InvalidObjectAddress;
7374 }
7375 }
7376
7377 /* skip if the name already exists and if_not_exists is true */
7378 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7379 {
7381 return InvalidObjectAddress;
7382 }
7383
7384 /*
7385 * Okay, we need to add the column, so go ahead and do parse
7386 * transformation. This can result in queueing up, or even immediately
7387 * executing, subsidiary operations (such as creation of unique indexes);
7388 * so we mustn't do it until we have made the if_not_exists check.
7389 *
7390 * When recursing, the command was already transformed and we needn't do
7391 * so again. Also, if context isn't given we can't transform. (That
7392 * currently happens only for AT_AddColumnToView; we expect that view.c
7393 * passed us a ColumnDef that doesn't need work.)
7394 */
7395 if (context != NULL && !recursing)
7396 {
7397 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7398 cur_pass, context);
7399 Assert(*cmd != NULL);
7400 colDef = castNode(ColumnDef, (*cmd)->def);
7401 }
7402
7403 /*
7404 * Regular inheritance children are independent enough not to inherit the
7405 * identity column from parent hence cannot recursively add identity
7406 * column if the table has inheritance children.
7407 *
7408 * Partitions, on the other hand, are integral part of a partitioned table
7409 * and inherit identity column. Hence propagate identity column down the
7410 * partition hierarchy.
7411 */
7412 if (colDef->identity &&
7413 recurse &&
7414 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7416 ereport(ERROR,
7418 errmsg("cannot recursively add identity column to table that has child tables")));
7419
7421
7424 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7426 relkind = relform->relkind;
7427
7428 /* Determine the new attribute's number */
7429 newattnum = relform->relnatts + 1;
7431 ereport(ERROR,
7433 errmsg("tables can have at most %d columns",
7435
7436 /*
7437 * Construct new attribute's pg_attribute entry.
7438 */
7440
7441 attribute = TupleDescAttr(tupdesc, 0);
7442
7443 /* Fix up attribute number */
7444 attribute->attnum = newattnum;
7445
7446 /* make sure datatype is legal for a column */
7447 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7448 list_make1_oid(rel->rd_rel->reltype),
7449 (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
7450
7452
7454
7455 /*
7456 * Update pg_class tuple as appropriate
7457 */
7458 relform->relnatts = newattnum;
7459
7461
7463
7464 /* Post creation hook for new attribute */
7466
7468
7469 /* Make the attribute's catalog entry visible */
7471
7472 /*
7473 * Store the DEFAULT, if any, in the catalogs
7474 */
7475 if (colDef->raw_default)
7476 {
7478
7480 rawEnt->attnum = attribute->attnum;
7481 rawEnt->raw_default = copyObject(colDef->raw_default);
7482 rawEnt->generated = colDef->generated;
7483
7484 /*
7485 * This function is intended for CREATE TABLE, so it processes a
7486 * _list_ of defaults, but we just do one.
7487 */
7489 false, true, false, NULL);
7490
7491 /* Make the additional catalog changes visible */
7493 }
7494
7495 /*
7496 * Tell Phase 3 to fill in the default expression, if there is one.
7497 *
7498 * If there is no default, Phase 3 doesn't have to do anything, because
7499 * that effectively means that the default is NULL. The heap tuple access
7500 * routines always check for attnum > # of attributes in tuple, and return
7501 * NULL if so, so without any modification of the tuple data we will get
7502 * the effect of NULL values in the new column.
7503 *
7504 * Note: we use build_column_default, and not just the cooked default
7505 * returned by AddRelationNewConstraints, so that the right thing happens
7506 * when a datatype's default applies.
7507 *
7508 * Note: it might seem that this should happen at the end of Phase 2, so
7509 * that the effects of subsequent subcommands can be taken into account.
7510 * It's intentional that we do it now, though. The new column should be
7511 * filled according to what is said in the ADD COLUMN subcommand, so that
7512 * the effects are the same as if this subcommand had been run by itself
7513 * and the later subcommands had been issued in new ALTER TABLE commands.
7514 *
7515 * We can skip this entirely for relations without storage, since Phase 3
7516 * is certainly not going to touch them.
7517 */
7518 if (RELKIND_HAS_STORAGE(relkind))
7519 {
7521 bool has_missing = false;
7522 bool has_volatile = false;
7523
7524 /*
7525 * For an identity column, we can't use build_column_default(),
7526 * because the sequence ownership isn't set yet. So do it manually.
7527 */
7528 if (colDef->identity)
7529 {
7531
7532 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7533 nve->typeId = attribute->atttypid;
7534
7535 defval = (Expr *) nve;
7536 }
7537 else
7538 defval = (Expr *) build_column_default(rel, attribute->attnum);
7539
7541 DomainHasConstraints(attribute->atttypid, &has_volatile);
7542
7543 /*
7544 * If the domain has volatile constraints, we must do a table rewrite
7545 * since the constraint result could differ per row and cannot be
7546 * evaluated once and cached as a missing value.
7547 */
7548 if (has_volatile)
7550
7551 /* Build CoerceToDomain(NULL) expression if needed */
7552 if (!defval && has_domain_constraints)
7553 {
7557
7558 baseTypeMod = attribute->atttypmod;
7559 baseTypeId = getBaseTypeAndTypmod(attribute->atttypid, &baseTypeMod);
7562 defval = (Expr *) coerce_to_target_type(NULL,
7563 (Node *) defval,
7564 baseTypeId,
7565 attribute->atttypid,
7566 attribute->atttypmod,
7569 -1);
7570 if (defval == NULL) /* should not happen */
7571 elog(ERROR, "failed to coerce base type to domain");
7572 }
7573
7574 if (defval)
7575 {
7577
7578 /* Prepare defval for execution, either here or in Phase 3 */
7579 defval = expression_planner(defval);
7580
7581 /* Add the new default to the newvals list */
7583 newval->attnum = attribute->attnum;
7584 newval->expr = defval;
7585 newval->is_generated = (colDef->generated != '\0');
7586
7587 tab->newvals = lappend(tab->newvals, newval);
7588
7589 /*
7590 * Attempt to skip a complete table rewrite by storing the
7591 * specified DEFAULT value outside of the heap. This is only
7592 * allowed for plain relations and non-generated columns, and the
7593 * default expression can't be volatile (stable is OK), and the
7594 * domain constraint expressions can't be volatile (stable is OK).
7595 *
7596 * Note that contain_volatile_functions considers CoerceToDomain
7597 * immutable, so we rely on DomainHasConstraints (called above)
7598 * rather than checking defval alone.
7599 *
7600 * For domains with non-volatile constraints, we evaluate the
7601 * default using soft error handling: if the constraint check
7602 * fails (e.g., CHECK(value > 10) with DEFAULT 8), we fall back to
7603 * a table rewrite. This preserves the historical behavior that
7604 * such a failure is only raised when the table has rows.
7605 */
7606 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7607 !colDef->generated &&
7608 !has_volatile &&
7609 !contain_volatile_functions((Node *) defval))
7610 {
7611 EState *estate;
7614 bool missingIsNull;
7616
7617 /* Evaluate the default expression with soft errors */
7618 estate = CreateExecutorState();
7619 exprState = ExecPrepareExprWithContext(defval, estate,
7620 (Node *) &escontext);
7622 GetPerTupleExprContext(estate),
7623 &missingIsNull);
7624
7625 /*
7626 * If the domain constraint check failed (via errsave),
7627 * missingval is unreliable. Fall back to a table rewrite;
7628 * Phase 3 will re-evaluate with hard errors, so the user gets
7629 * an error only if the table has rows.
7630 */
7631 if (SOFT_ERROR_OCCURRED(&escontext))
7632 {
7633 missingIsNull = true;
7635 }
7636
7637 /* If it turns out NULL, nothing to do; else store it */
7638 if (!missingIsNull)
7639 {
7640 StoreAttrMissingVal(rel, attribute->attnum, missingval);
7641 /* Make the additional catalog change visible */
7643 has_missing = true;
7644 }
7645 FreeExecutorState(estate);
7646 }
7647 else
7648 {
7649 /*
7650 * Failed to use missing mode. We have to do a table rewrite
7651 * to install the value --- unless it's a virtual generated
7652 * column.
7653 */
7654 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7656 }
7657 }
7658
7659 if (!has_missing)
7660 {
7661 /*
7662 * If the new column is NOT NULL, and there is no missing value,
7663 * tell Phase 3 it needs to check for NULLs.
7664 */
7665 tab->verify_new_notnull |= colDef->is_not_null;
7666 }
7667 }
7668
7669 /*
7670 * Add needed dependency entries for the new column.
7671 */
7672 add_column_datatype_dependency(myrelid, newattnum, attribute->atttypid);
7673 add_column_collation_dependency(myrelid, newattnum, attribute->attcollation);
7674
7675 /*
7676 * Propagate to children as appropriate. Unlike most other ALTER
7677 * routines, we have to do this one level of recursion at a time; we can't
7678 * use find_all_inheritors to do it in one pass.
7679 */
7680 children =
7682
7683 /*
7684 * If we are told not to recurse, there had better not be any child
7685 * tables; else the addition would put them out of step.
7686 */
7687 if (children && !recurse)
7688 ereport(ERROR,
7690 errmsg("column must be added to child tables too")));
7691
7692 /* Children should see column as singly inherited */
7693 if (!recursing)
7694 {
7695 childcmd = copyObject(*cmd);
7697 colDef->inhcount = 1;
7698 colDef->is_local = false;
7699 }
7700 else
7701 childcmd = *cmd; /* no need to copy again */
7702
7703 foreach(child, children)
7704 {
7705 Oid childrelid = lfirst_oid(child);
7708
7709 /* find_inheritance_children already got lock */
7712
7713 /* Find or create work queue entry for this table */
7715
7716 /* Recurse to child; return value is ignored */
7718 &childcmd, recurse, true,
7719 lockmode, cur_pass, context);
7720
7722 }
7723
7725 return address;
7726}
bool contain_volatile_functions(Node *clause)
Definition clauses.c:549
#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:660
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:396
#define palloc_object(type)
Definition fe_memutils.h:74
#define newval
void CheckAttributeType(const char *attname, Oid atttypid, Oid attcollation, List *containing_rowtypes, int flags)
Definition heap.c:544
void InsertPgAttributeTuples(Relation pg_attribute_rel, TupleDesc tupdesc, Oid new_rel_oid, const FormExtraData_pg_attribute tupdesc_extra[], CatalogIndexState indstate)
Definition heap.c:717
void StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
Definition heap.c:2030
#define CHKATYPE_IS_VIRTUAL
Definition heap.h:26
#define MaxHeapAttributeNumber
static bool pg_add_s16_overflow(int16 a, int16 b, int16 *result)
Definition int.h:67
Oid get_typcollation(Oid typid)
Definition lsyscache.c:3278
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition lsyscache.c:2760
Const * makeNullConst(Oid consttype, int32 consttypmod, Oid constcollid)
Definition makefuncs.c:388
#define SOFT_ERROR_OCCURRED(escontext)
Definition miscnodes.h:53
#define RangeVarGetRelid(relation, lockmode, missing_ok)
Definition namespace.h:98
#define castNode(_type_, nodeptr)
Definition nodes.h:182
#define InvokeObjectPostCreateHook(classId, objectId, subId)
#define ObjectAddressSubSet(addr, class_id, object_id, object_sub_id)
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
void typenameTypeIdAndMod(ParseState *pstate, const TypeName *typeName, Oid *typeid_p, int32 *typmod_p)
Definition parse_type.c:310
Oid GetColumnDefCollation(ParseState *pstate, const ColumnDef *coldef, Oid typeOid)
Definition parse_type.c:540
#define list_make1_oid(x1)
Definition pg_list.h:242
Expr * expression_planner(Expr *expr)
Definition planner.c:6819
uint64_t Datum
Definition postgres.h:70
@ COERCE_IMPLICIT_CAST
Definition primnodes.h:769
@ COERCION_ASSIGNMENT
Definition primnodes.h:748
Node * build_column_default(Relation rel, int attrno)
bool verify_new_notnull
Definition tablecmds.c:192
ItemPointerData t_self
Definition htup.h:65
HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname)
Definition syscache.c:498
#define SearchSysCacheCopy1(cacheId, key1)
Definition syscache.h:91
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition tablecmds.c:7786
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition tablecmds.c:7804
TupleDesc BuildDescForRelation(const List *columns)
Definition tablecmds.c:1403
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition tablecmds.c:5769
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition tablecmds.c:7733
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:7279

References add_column_collation_dependency(), add_column_datatype_dependency(), AddRelationNewConstraints(), Assert, AT_REWRITE_DEFAULT_VAL, ATExecAddColumn(), ATGetQueueEntry(), ATParseTransformCmd(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, build_column_default(), BuildDescForRelation(), castNode, CatalogTupleUpdate(), check_for_column_name_collision(), check_stack_depth(), CheckAlterTableIsSafe(), CheckAttributeType(), CHKATYPE_IS_VIRTUAL, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, CommandCounterIncrement(), contain_volatile_functions(), copyObject, CreateExecutorState(), DomainHasConstraints(), elog, ereport, errcode(), errdetail(), errmsg, ERROR, ExecEvalExpr(), 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(), SOFT_ERROR_OCCURRED, 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 9871 of file tablecmds.c.

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

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

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

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

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

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

17500{
17502 List *children;
17503 ObjectAddress address;
17504 const char *trigger_name;
17505
17506 /*
17507 * A self-exclusive lock is needed here. See the similar case in
17508 * MergeAttributes() for a full explanation.
17509 */
17511
17512 /*
17513 * Must be owner of both parent and child -- child was checked by
17514 * ATSimplePermissions call in ATPrepCmd
17515 */
17518
17519 /* Permanent rels cannot inherit from temporary ones */
17520 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17521 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17522 ereport(ERROR,
17524 errmsg("cannot inherit from temporary relation \"%s\"",
17526
17527 /* If parent rel is temp, it must belong to this session */
17529 ereport(ERROR,
17531 errmsg("cannot inherit from temporary relation of another session")));
17532
17533 /* Ditto for the child */
17535 ereport(ERROR,
17537 errmsg("cannot inherit to temporary relation of another session")));
17538
17539 /* Prevent partitioned tables from becoming inheritance parents */
17540 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17541 ereport(ERROR,
17543 errmsg("cannot inherit from partitioned table \"%s\"",
17544 parent->relname)));
17545
17546 /* Likewise for partitions */
17547 if (parent_rel->rd_rel->relispartition)
17548 ereport(ERROR,
17550 errmsg("cannot inherit from a partition")));
17551
17552 /*
17553 * Prevent circularity by seeing if proposed parent inherits from child.
17554 * (In particular, this disallows making a rel inherit from itself.)
17555 *
17556 * This is not completely bulletproof because of race conditions: in
17557 * multi-level inheritance trees, someone else could concurrently be
17558 * making another inheritance link that closes the loop but does not join
17559 * either of the rels we have locked. Preventing that seems to require
17560 * exclusive locks on the entire inheritance tree, which is a cure worse
17561 * than the disease. find_all_inheritors() will cope with circularity
17562 * anyway, so don't sweat it too much.
17563 *
17564 * We use weakest lock we can on child's children, namely AccessShareLock.
17565 */
17568
17570 ereport(ERROR,
17572 errmsg("circular inheritance not allowed"),
17573 errdetail("\"%s\" is already a child of \"%s\".",
17574 parent->relname,
17576
17577 /*
17578 * If child_rel has row-level triggers with transition tables, we
17579 * currently don't allow it to become an inheritance child. See also
17580 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17581 */
17583 if (trigger_name != NULL)
17584 ereport(ERROR,
17586 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17588 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17589
17590 /* OK to create inheritance */
17592
17595
17596 /* keep our lock on the parent relation until commit */
17598
17599 return address;
17600}
#define RELATION_IS_OTHER_TEMP(relation)
Definition rel.h:667
char * relname
Definition primnodes.h:84
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
const char * FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc)
Definition trigger.c: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 18450 of file tablecmds.c.

18451{
18452 Oid relid = RelationGetRelid(rel);
18455 Oid typeid;
18458 SysScanDesc scan;
18461 type_attno;
18465 typeobj;
18467
18468 /* Validate the type. */
18469 typetuple = typenameType(NULL, ofTypename, NULL);
18472 typeid = typeform->oid;
18473
18474 /* Fail if the table has any inheritance parents. */
18476 ScanKeyInit(&key,
18479 ObjectIdGetDatum(relid));
18481 true, NULL, 1, &key);
18483 ereport(ERROR,
18485 errmsg("typed tables cannot inherit")));
18486 systable_endscan(scan);
18488
18489 /*
18490 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18491 * require that the order also match. However, attnotnull need not match.
18492 */
18495 table_attno = 1;
18496 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18497 {
18499 table_attr;
18500 const char *type_attname,
18502
18503 /* Get the next non-dropped type attribute. */
18505 if (type_attr->attisdropped)
18506 continue;
18507 type_attname = NameStr(type_attr->attname);
18508
18509 /* Get the next non-dropped table attribute. */
18510 do
18511 {
18512 if (table_attno > tableTupleDesc->natts)
18513 ereport(ERROR,
18515 errmsg("table is missing column \"%s\"",
18516 type_attname)));
18518 table_attno++;
18519 } while (table_attr->attisdropped);
18520 table_attname = NameStr(table_attr->attname);
18521
18522 /* Compare name. */
18524 ereport(ERROR,
18526 errmsg("table has column \"%s\" where type requires \"%s\"",
18528
18529 /* Compare type. */
18530 if (table_attr->atttypid != type_attr->atttypid ||
18531 table_attr->atttypmod != type_attr->atttypmod ||
18532 table_attr->attcollation != type_attr->attcollation)
18533 ereport(ERROR,
18535 errmsg("table \"%s\" has different type for column \"%s\"",
18537 }
18539
18540 /* Any remaining columns at the end of the table had better be dropped. */
18541 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18542 {
18544 table_attno - 1);
18545
18546 if (!table_attr->attisdropped)
18547 ereport(ERROR,
18549 errmsg("table has extra column \"%s\"",
18550 NameStr(table_attr->attname))));
18551 }
18552
18553 /* If the table was already typed, drop the existing dependency. */
18554 if (rel->rd_rel->reloftype)
18555 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18557
18558 /* Record a dependency on the new type. */
18559 tableobj.classId = RelationRelationId;
18560 tableobj.objectId = relid;
18561 tableobj.objectSubId = 0;
18562 typeobj.classId = TypeRelationId;
18563 typeobj.objectId = typeid;
18564 typeobj.objectSubId = 0;
18566
18567 /* Update pg_class.reloftype */
18571 elog(ERROR, "cache lookup failed for relation %u", relid);
18572 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18574
18576
18579
18581
18582 return typeobj;
18583}
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:7205
#define ReleaseTupleDesc(tupdesc)
Definition tupdesc.h:238
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 9758 of file tablecmds.c.

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

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

12639{
12641 Relation rel;
12642 bool changed = false;
12643 List *children = NIL;
12644
12645 /* Since this function recurses, it could be driven to stack overflow */
12647
12648 Assert(cmdcon->alterEnforceability);
12649
12651
12652 Assert(currcon->contype == CONSTRAINT_CHECK);
12653
12654 /*
12655 * Parent relation already locked by caller, children will be locked by
12656 * find_all_inheritors. So NoLock is fine here.
12657 */
12658 rel = table_open(currcon->conrelid, NoLock);
12659
12660 if (currcon->conenforced != cmdcon->is_enforced)
12661 {
12663 changed = true;
12664 }
12665
12666 /*
12667 * Note that we must recurse even when trying to change a check constraint
12668 * to not enforced if it is already not enforced, in case descendant
12669 * constraints might be enforced and need to be changed to not enforced.
12670 * Conversely, we should do nothing if a constraint is being set to
12671 * enforced and is already enforced, as descendant constraints cannot be
12672 * different in that case.
12673 */
12674 if (!cmdcon->is_enforced || changed)
12675 {
12676 /*
12677 * If we're recursing, the parent has already done this, so skip it.
12678 * Also, if the constraint is a NO INHERIT constraint, we shouldn't
12679 * try to look for it in the children.
12680 */
12681 if (!recursing && !currcon->connoinherit)
12682 children = find_all_inheritors(RelationGetRelid(rel),
12683 lockmode, NULL);
12684
12685 foreach_oid(childoid, children)
12686 {
12687 if (childoid == RelationGetRelid(rel))
12688 continue;
12689
12690 /*
12691 * If we are told not to recurse, there had better not be any
12692 * child tables, because we can't change constraint enforceability
12693 * on the parent unless we have changed enforceability for all
12694 * child.
12695 */
12696 if (!recurse)
12697 ereport(ERROR,
12699 errmsg("constraint must be altered on child tables too"),
12700 errhint("Do not specify the ONLY keyword."));
12701
12703 childoid, false, true,
12704 lockmode);
12705 }
12706 }
12707
12708 /*
12709 * Tell Phase 3 to check that the constraint is satisfied by existing
12710 * rows. We only need do this when altering the constraint from NOT
12711 * ENFORCED to ENFORCED.
12712 */
12713 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12714 !currcon->conenforced &&
12715 cmdcon->is_enforced)
12716 {
12717 AlteredTableInfo *tab;
12719 Datum val;
12720 char *conbin;
12721
12723 newcon->name = pstrdup(NameStr(currcon->conname));
12724 newcon->contype = CONSTR_CHECK;
12725
12730
12731 /* Find or create work queue entry for this table */
12732 tab = ATGetQueueEntry(wqueue, rel);
12733 tab->constraints = lappend(tab->constraints, newcon);
12734 }
12735
12736 table_close(rel, NoLock);
12737
12738 return changed;
12739}
#define TextDatumGetCString(d)
Definition builtins.h:99
long val
Definition informix.c:689
#define foreach_oid(var, lst)
Definition pg_list.h:471
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:625
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 16197 of file tablecmds.c.

16201{
16204 ForeignServer *server;
16206 HeapTuple tuple;
16207 HeapTuple newtuple;
16208 bool isnull;
16212 Datum datum;
16216 ObjectAddress address;
16217
16218 if (options == NIL)
16219 return InvalidObjectAddress;
16220
16221 /* First, determine FDW validator associated to the foreign table. */
16224 if (!HeapTupleIsValid(tuple))
16225 ereport(ERROR,
16227 errmsg("foreign table \"%s\" does not exist",
16230 server = GetForeignServer(fttableform->ftserver);
16231 fdw = GetForeignDataWrapper(server->fdwid);
16232
16234 ReleaseSysCache(tuple);
16235
16238 if (!HeapTupleIsValid(tuple))
16239 ereport(ERROR,
16241 errmsg("column \"%s\" of relation \"%s\" does not exist",
16243
16244 /* Prevent them from altering a system attribute */
16246 attnum = atttableform->attnum;
16247 if (attnum <= 0)
16248 ereport(ERROR,
16250 errmsg("cannot alter system column \"%s\"", colName)));
16251
16252
16253 /* Initialize buffers for new tuple values */
16254 memset(repl_val, 0, sizeof(repl_val));
16255 memset(repl_null, false, sizeof(repl_null));
16256 memset(repl_repl, false, sizeof(repl_repl));
16257
16258 /* Extract the current options */
16259 datum = SysCacheGetAttr(ATTNAME,
16260 tuple,
16262 &isnull);
16263 if (isnull)
16264 datum = PointerGetDatum(NULL);
16265
16266 /* Transform the options */
16268 datum,
16269 options,
16270 fdw->fdwvalidator);
16271
16272 if (DatumGetPointer(datum) != NULL)
16274 else
16276
16278
16279 /* Everything looks good - update the tuple */
16280
16281 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16283
16284 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16285
16287 RelationGetRelid(rel),
16288 atttableform->attnum);
16290 RelationGetRelid(rel), attnum);
16291
16292 ReleaseSysCache(tuple);
16293
16295
16296 heap_freetuple(newtuple);
16297
16298 return address;
16299}
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:1130
END_CATALOG_STRUCT typedef FormData_pg_foreign_table * Form_pg_foreign_table
static Datum PointerGetDatum(const void *X)
Definition postgres.h:342
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
Oid rd_id
Definition rel.h:113
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition syscache.c:475
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:595

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

Referenced by ATExecCmd().

◆ ATExecAlterColumnType()

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

Definition at line 14968 of file tablecmds.c.

14970{
14971 char *colName = cmd->name;
14972 ColumnDef *def = (ColumnDef *) cmd->def;
14973 TypeName *typeName = def->typeName;
14976 attOldTup;
14980 Oid targettype;
14981 int32 targettypmod;
14986 ScanKeyData key[3];
14987 SysScanDesc scan;
14989 ObjectAddress address;
14990
14991 /*
14992 * Clear all the missing values if we're rewriting the table, since this
14993 * renders them pointless.
14994 */
14995 if (tab->rewrite)
14996 {
14998
15002 /* make sure we don't conflict with later attribute modifications */
15004 }
15005
15007
15008 /* Look up the target column */
15010 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
15011 ereport(ERROR,
15013 errmsg("column \"%s\" of relation \"%s\" does not exist",
15016 attnum = attTup->attnum;
15018
15019 /* Check for multiple ALTER TYPE on same column --- can't cope */
15020 if (attTup->atttypid != attOldTup->atttypid ||
15021 attTup->atttypmod != attOldTup->atttypmod)
15022 ereport(ERROR,
15024 errmsg("cannot alter type of column \"%s\" twice",
15025 colName)));
15026
15027 /* Look up the target type (should not fail, since prep found it) */
15028 typeTuple = typenameType(NULL, typeName, &targettypmod);
15030 targettype = tform->oid;
15031 /* And the collation */
15032 targetcollid = GetColumnDefCollation(NULL, def, targettype);
15033
15034 /*
15035 * If there is a default expression for the column, get it and ensure we
15036 * can coerce it to the new datatype. (We must do this before changing
15037 * the column type, because build_column_default itself will try to
15038 * coerce, and will not issue the error message we want if it fails.)
15039 *
15040 * We remove any implicit coercion steps at the top level of the old
15041 * default expression; this has been agreed to satisfy the principle of
15042 * least surprise. (The conversion to the new column type should act like
15043 * it started from what the user sees as the stored expression, and the
15044 * implicit coercions aren't going to be shown.)
15045 */
15046 if (attTup->atthasdef)
15047 {
15051 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
15053 targettype, targettypmod,
15056 -1);
15057 if (defaultexpr == NULL)
15058 {
15059 if (attTup->attgenerated)
15060 ereport(ERROR,
15062 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
15063 colName, format_type_be(targettype))));
15064 else
15065 ereport(ERROR,
15067 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
15068 colName, format_type_be(targettype))));
15069 }
15070 }
15071 else
15072 defaultexpr = NULL;
15073
15074 /*
15075 * Find everything that depends on the column (constraints, indexes, etc),
15076 * and record enough information to let us recreate the objects.
15077 *
15078 * The actual recreation does not happen here, but only after we have
15079 * performed all the individual ALTER TYPE operations. We have to save
15080 * the info before executing ALTER TYPE, though, else the deparser will
15081 * get confused.
15082 */
15084
15085 /*
15086 * Now scan for dependencies of this column on other things. The only
15087 * things we should find are the dependency on the column datatype and
15088 * possibly a collation dependency. Those can be removed.
15089 */
15091
15092 ScanKeyInit(&key[0],
15096 ScanKeyInit(&key[1],
15100 ScanKeyInit(&key[2],
15104
15106 NULL, 3, key);
15107
15109 {
15112
15113 foundObject.classId = foundDep->refclassid;
15114 foundObject.objectId = foundDep->refobjid;
15115 foundObject.objectSubId = foundDep->refobjsubid;
15116
15117 if (foundDep->deptype != DEPENDENCY_NORMAL)
15118 elog(ERROR, "found unexpected dependency type '%c'",
15119 foundDep->deptype);
15120 if (!(foundDep->refclassid == TypeRelationId &&
15121 foundDep->refobjid == attTup->atttypid) &&
15122 !(foundDep->refclassid == CollationRelationId &&
15123 foundDep->refobjid == attTup->attcollation))
15124 elog(ERROR, "found unexpected dependency for column: %s",
15126
15127 CatalogTupleDelete(depRel, &depTup->t_self);
15128 }
15129
15130 systable_endscan(scan);
15131
15133
15134 /*
15135 * Here we go --- change the recorded column type and collation. (Note
15136 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
15137 * fix up the missing value if any.
15138 */
15139 if (attTup->atthasmissing)
15140 {
15142 bool missingNull;
15143
15144 /* if rewrite is true the missing value should already be cleared */
15145 Assert(tab->rewrite == 0);
15146
15147 /* Get the missing value datum */
15150 attrelation->rd_att,
15151 &missingNull);
15152
15153 /* if it's a null array there is nothing to do */
15154
15155 if (!missingNull)
15156 {
15157 /*
15158 * Get the datum out of the array and repack it in a new array
15159 * built with the new type data. We assume that since the table
15160 * doesn't need rewriting, the actual Datum doesn't need to be
15161 * changed, only the array metadata.
15162 */
15163
15164 int one = 1;
15165 bool isNull;
15167 bool nullsAtt[Natts_pg_attribute] = {0};
15168 bool replacesAtt[Natts_pg_attribute] = {0};
15170
15172 1,
15173 &one,
15174 0,
15175 attTup->attlen,
15176 attTup->attbyval,
15177 attTup->attalign,
15178 &isNull);
15180 1,
15181 targettype,
15182 tform->typlen,
15183 tform->typbyval,
15184 tform->typalign));
15185
15189
15193 heapTup = newTup;
15195 }
15196 }
15197
15198 attTup->atttypid = targettype;
15199 attTup->atttypmod = targettypmod;
15200 attTup->attcollation = targetcollid;
15201 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
15202 ereport(ERROR,
15204 errmsg("too many array dimensions"));
15205 attTup->attndims = list_length(typeName->arrayBounds);
15206 attTup->attlen = tform->typlen;
15207 attTup->attbyval = tform->typbyval;
15208 attTup->attalign = tform->typalign;
15209 attTup->attstorage = tform->typstorage;
15210 attTup->attcompression = InvalidCompressionMethod;
15211
15213
15215
15217
15218 /* Install dependencies on new datatype and collation */
15221
15222 /*
15223 * Drop any pg_statistic entry for the column, since it's now wrong type
15224 */
15226
15228 RelationGetRelid(rel), attnum);
15229
15230 /*
15231 * Update the default, if present, by brute force --- remove and re-add
15232 * the default. Probably unsafe to take shortcuts, since the new version
15233 * may well have additional dependencies. (It's okay to do this now,
15234 * rather than after other ALTER TYPE commands, since the default won't
15235 * depend on other column types.)
15236 */
15237 if (defaultexpr)
15238 {
15239 /*
15240 * If it's a GENERATED default, drop its dependency records, in
15241 * particular its INTERNAL dependency on the column, which would
15242 * otherwise cause dependency.c to refuse to perform the deletion.
15243 */
15244 if (attTup->attgenerated)
15245 {
15247
15248 if (!OidIsValid(attrdefoid))
15249 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15250 RelationGetRelid(rel), attnum);
15252 }
15253
15254 /*
15255 * Make updates-so-far visible, particularly the new pg_attribute row
15256 * which will be updated again.
15257 */
15259
15260 /*
15261 * We use RESTRICT here for safety, but at present we do not expect
15262 * anything to depend on the default.
15263 */
15265 true);
15266
15267 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15268 }
15269
15271 RelationGetRelid(rel), attnum);
15272
15273 /* Cleanup */
15275
15276 return address;
15277}
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:672
void RelationClearMissing(Relation rel)
Definition heap.c:1964
void RemoveStatistics(Oid relid, AttrNumber attnum)
Definition heap.c:3495
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
void CatalogTupleDelete(Relation heapRel, const ItemPointerData *tid)
Definition indexing.c:365
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
Node * strip_implicit_coercions(Node *node)
Definition nodeFuncs.c: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:291
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
#define InvalidCompressionMethod

References add_column_collation_dependency(), add_column_datatype_dependency(), array_get_element(), TypeName::arrayBounds, Assert, AT_AlterColumnType, attnum, BTEqualStrategyNumber, build_column_default(), CatalogTupleDelete(), CatalogTupleUpdate(), ObjectAddress::classId, COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, CommandCounterIncrement(), construct_array(), AlterTableCmd::def, deleteDependencyRecordsFor(), DEPENDENCY_NORMAL, DROP_RESTRICT, elog, ereport, errcode(), errmsg, ERROR, exprType(), fb(), Form_pg_depend, Form_pg_type, format_type_be(), GetAttrDefaultOid(), GetColumnDefCollation(), getObjectDescription(), GETSTRUCT(), heap_freetuple(), heap_getattr(), heap_modify_tuple(), HeapTupleIsValid, Int32GetDatum(), InvalidCompressionMethod, InvokeObjectPostAlterHook, list_length(), AlterTableCmd::name, NoLock, ObjectAddressSubSet, ObjectIdGetDatum(), OidIsValid, AlteredTableInfo::oldDesc, PG_INT16_MAX, PointerGetDatum(), relation_close(), RelationClearMissing(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RememberAllDependentForRebuilding(), RemoveAttrDefault(), RemoveStatistics(), AlteredTableInfo::rewrite, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopyAttName(), StoreAttrDefault(), strip_implicit_coercions(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TupleDescAttr(), ColumnDef::typeName, and typenameType().

Referenced by ATExecCmd().

◆ ATExecAlterConstraint()

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

Definition at line 12273 of file tablecmds.c.

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

12422{
12424 bool changed = false;
12425 List *otherrelids = NIL;
12426
12428
12429 /*
12430 * Do the catalog work for the enforceability or deferrability change,
12431 * recurse if necessary.
12432 *
12433 * Note that even if deferrability is requested to be altered along with
12434 * enforceability, we don't need to explicitly update multiple entries in
12435 * pg_trigger related to deferrability.
12436 *
12437 * Modifying foreign key enforceability involves either creating or
12438 * dropping the trigger, during which the deferrability setting will be
12439 * adjusted automatically.
12440 */
12441 if (cmdcon->alterEnforceability)
12442 {
12443 if (currcon->contype == CONSTRAINT_FOREIGN)
12445 currcon->conrelid,
12446 currcon->confrelid,
12447 contuple, lockmode,
12450 else if (currcon->contype == CONSTRAINT_CHECK)
12452 contuple, recurse, false,
12453 lockmode);
12454 }
12455 else if (cmdcon->alterDeferrability &&
12457 contuple, recurse, &otherrelids,
12458 lockmode))
12459 {
12460 /*
12461 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12462 * the relations having the constraint itself; here we also invalidate
12463 * for relations that have any triggers that are part of the
12464 * constraint.
12465 */
12466 foreach_oid(relid, otherrelids)
12468
12469 changed = true;
12470 }
12471
12472 /*
12473 * Do the catalog work for the inheritability change.
12474 */
12475 if (cmdcon->alterInheritability &&
12477 lockmode))
12478 changed = true;
12479
12480 return changed;
12481}
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 12800 of file tablecmds.c.

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

12860{
12863 char *colName;
12864 List *children;
12865
12866 Assert(cmdcon->alterInheritability);
12867
12869
12870 /* The current implementation only works for NOT NULL constraints */
12871 Assert(currcon->contype == CONSTRAINT_NOTNULL);
12872
12873 /*
12874 * If called to modify a constraint that's already in the desired state,
12875 * silently do nothing.
12876 */
12877 if (cmdcon->noinherit == currcon->connoinherit)
12878 return false;
12879
12882
12883 /* Fetch the column number and name */
12885 colName = get_attname(currcon->conrelid, colNum, false);
12886
12887 /*
12888 * Propagate the change to children. For this subcommand type we don't
12889 * recursively affect children, just the immediate level.
12890 */
12892 lockmode);
12893 foreach_oid(childoid, children)
12894 {
12895 ObjectAddress addr;
12896
12897 if (cmdcon->noinherit)
12898 {
12901
12903 if (!childtup)
12904 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
12905 colName, childoid);
12907 Assert(childcon->coninhcount > 0);
12908 childcon->coninhcount--;
12909 childcon->conislocal = true;
12912 }
12913 else
12914 {
12916
12917 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
12918 colName, true, true, lockmode);
12919 if (OidIsValid(addr.objectId))
12922 }
12923 }
12924
12925 return true;
12926}
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:8000

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

12504{
12506 Oid conoid;
12507 Relation rel;
12508 bool changed = false;
12509
12510 /* Since this function recurses, it could be driven to stack overflow */
12512
12513 Assert(cmdcon->alterEnforceability);
12514
12516 conoid = currcon->oid;
12517
12518 /* Should be foreign key constraint */
12519 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12520
12521 rel = table_open(currcon->conrelid, lockmode);
12522
12523 if (currcon->conenforced != cmdcon->is_enforced)
12524 {
12526 changed = true;
12527 }
12528
12529 /* Drop triggers */
12530 if (!cmdcon->is_enforced)
12531 {
12532 /*
12533 * When setting a constraint to NOT ENFORCED, the constraint triggers
12534 * need to be dropped. Therefore, we must process the child relations
12535 * first, followed by the parent, to account for dependencies.
12536 */
12537 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12541 lockmode, InvalidOid, InvalidOid,
12543
12544 /* Drop all the triggers */
12546 }
12547 else if (changed) /* Create triggers */
12548 {
12553
12554 /* Prepare the minimal information required for trigger creation. */
12556
12557 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12558 fkconstraint->fk_matchtype = currcon->confmatchtype;
12559 fkconstraint->fk_upd_action = currcon->confupdtype;
12560 fkconstraint->fk_del_action = currcon->confdeltype;
12561
12562 /* Create referenced triggers */
12563 if (currcon->conrelid == fkrelid)
12565 currcon->confrelid,
12567 conoid,
12568 currcon->conindid,
12573
12574 /* Create referencing triggers */
12575 if (currcon->confrelid == pkrelid)
12577 pkrelid,
12579 conoid,
12580 currcon->conindid,
12585
12586 /*
12587 * Tell Phase 3 to check that the constraint is satisfied by existing
12588 * rows. Only applies to leaf partitions, and (for constraints that
12589 * reference a partitioned table) only if this is not one of the
12590 * pg_constraint rows that exist solely to support action triggers.
12591 */
12592 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12593 currcon->confrelid == pkrelid)
12594 {
12595 AlteredTableInfo *tab;
12597
12599 newcon->name = fkconstraint->conname;
12600 newcon->contype = CONSTR_FOREIGN;
12601 newcon->refrelid = currcon->confrelid;
12602 newcon->refindid = currcon->conindid;
12603 newcon->conid = currcon->oid;
12604 newcon->qual = (Node *) fkconstraint;
12605
12606 /* Find or create work queue entry for this table */
12607 tab = ATGetQueueEntry(wqueue, rel);
12608 tab->constraints = lappend(tab->constraints, newcon);
12609 }
12610
12611 /*
12612 * If the table at either end of the constraint is partitioned, we
12613 * need to recurse and create triggers for each constraint that is a
12614 * child of this one.
12615 */
12616 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12620 lockmode,
12625 }
12626
12627 table_close(rel, NoLock);
12628
12629 return changed;
12630}
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 20523 of file tablecmds.c.

20525{
20527 catalog;
20530 SysScanDesc scan;
20532 AttrNumber attno;
20533 int natts;
20535 ObjectAddress address;
20536 const char *trigger_name;
20540 ParseState *pstate = make_parsestate(NULL);
20541
20542 pstate->p_sourcetext = context->queryString;
20543
20544 /*
20545 * We must lock the default partition if one exists, because attaching a
20546 * new partition will change its partition constraint.
20547 */
20552
20554
20555 /*
20556 * XXX I think it'd be a good idea to grab locks on all tables referenced
20557 * by FKs at this point also.
20558 */
20559
20560 /*
20561 * Must be owner of both parent and source table -- parent was checked by
20562 * ATSimplePermissions call in ATPrepCmd
20563 */
20566
20567 /* A partition can only have one parent */
20568 if (attachrel->rd_rel->relispartition)
20569 ereport(ERROR,
20571 errmsg("\"%s\" is already a partition",
20573
20574 if (OidIsValid(attachrel->rd_rel->reloftype))
20575 ereport(ERROR,
20577 errmsg("cannot attach a typed table as partition")));
20578
20579 /*
20580 * Disallow attaching a partition if the table is referenced in a
20581 * publication EXCEPT clause. Changing the partition hierarchy could alter
20582 * the effective publication membership.
20583 */
20585 if (exceptpuboids != NIL)
20586 {
20587 bool first = true;
20589
20591
20593 {
20594 char *pubname = get_publication_name(pubid, false);
20595
20596 if (!first)
20597 {
20598 /*
20599 * translator: This is a separator in a list of publication
20600 * names.
20601 */
20603 }
20604
20605 first = false;
20606
20607 appendStringInfo(&pubnames, _("\"%s\""), pubname);
20608 }
20609
20610 ereport(ERROR,
20612 errmsg_plural("cannot attach table \"%s\" as partition because it is referenced in publication %s EXCEPT clause",
20613 "cannot attach table \"%s\" as partition because it is referenced in publications %s EXCEPT clause",
20616 pubnames.data),
20617 errdetail("The publication EXCEPT clause cannot contain tables that are partitions."),
20618 errhint("Change the publication's EXCEPT clause using ALTER PUBLICATION ... SET ALL TABLES."));
20619 }
20620
20622
20623 /*
20624 * Table being attached should not already be part of inheritance; either
20625 * as a child table...
20626 */
20633 NULL, 1, &skey);
20635 ereport(ERROR,
20637 errmsg("cannot attach inheritance child as partition")));
20638 systable_endscan(scan);
20639
20640 /* ...or as a parent table (except the case when it is partitioned) */
20646 1, &skey);
20648 attachrel->rd_rel->relkind == RELKIND_RELATION)
20649 ereport(ERROR,
20651 errmsg("cannot attach inheritance parent as partition")));
20652 systable_endscan(scan);
20654
20655 /*
20656 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20657 * particular, this disallows making a rel a partition of itself.)
20658 *
20659 * We do that by checking if rel is a member of the list of attachrel's
20660 * partitions provided the latter is partitioned at all. We want to avoid
20661 * having to construct this list again, so we request the strongest lock
20662 * on all partitions. We need the strongest lock, because we may decide
20663 * to scan them if we find out that the table being attached (or its leaf
20664 * partitions) may contain rows that violate the partition constraint. If
20665 * the table has a constraint that would prevent such rows, which by
20666 * definition is present in all the partitions, we need not scan the
20667 * table, nor its partitions. But we cannot risk a deadlock by taking a
20668 * weaker lock now and the stronger one only when needed.
20669 */
20673 ereport(ERROR,
20675 errmsg("circular inheritance not allowed"),
20676 errdetail("\"%s\" is already a child of \"%s\".",
20679
20680 /* If the parent is permanent, so must be all of its partitions. */
20681 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20682 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20683 ereport(ERROR,
20685 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20687
20688 /* Temp parent cannot have a partition that is itself not a temp */
20689 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20690 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20691 ereport(ERROR,
20693 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20695
20696 /* If the parent is temp, it must belong to this session */
20697 if (RELATION_IS_OTHER_TEMP(rel))
20698 ereport(ERROR,
20700 errmsg("cannot attach as partition of temporary relation of another session")));
20701
20702 /* Ditto for the partition */
20704 ereport(ERROR,
20706 errmsg("cannot attach temporary relation of another session as partition")));
20707
20708 /*
20709 * Check if attachrel has any identity columns or any columns that aren't
20710 * in the parent.
20711 */
20713 natts = tupleDesc->natts;
20714 for (attno = 1; attno <= natts; attno++)
20715 {
20716 Form_pg_attribute attribute = TupleDescAttr(tupleDesc, attno - 1);
20717 char *attributeName = NameStr(attribute->attname);
20718
20719 /* Ignore dropped */
20720 if (attribute->attisdropped)
20721 continue;
20722
20723 if (attribute->attidentity)
20724 ereport(ERROR,
20726 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20728 errdetail("The new partition may not contain an identity column."));
20729
20730 /* Try to find the column in parent (matching on column name) */
20734 ereport(ERROR,
20736 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20739 errdetail("The new partition may contain only the columns present in parent.")));
20740 }
20741
20742 /*
20743 * If child_rel has row-level triggers with transition tables, we
20744 * currently don't allow it to become a partition. See also prohibitions
20745 * in ATExecAddInherit() and CreateTrigger().
20746 */
20748 if (trigger_name != NULL)
20749 ereport(ERROR,
20751 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20753 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20754
20755 /*
20756 * Check that the new partition's bound is valid and does not overlap any
20757 * of existing partitions of the parent - note that it does not return on
20758 * error.
20759 */
20761 cmd->bound, pstate);
20762
20764
20765 /*
20766 * Generate a partition constraint from the partition bound specification.
20767 * If the parent itself is a partition, make sure to include its
20768 * constraint as well.
20769 */
20771
20772 /*
20773 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20774 * since it's needed later to construct the constraint expression for
20775 * validating against the default partition, if any.
20776 */
20779
20780 /* Skip validation if there are no constraints to validate. */
20781 if (partConstraint)
20782 {
20783 /*
20784 * Run the partition quals through const-simplification similar to
20785 * check constraints. We skip canonicalize_qual, though, because
20786 * partition quals should be in canonical form already.
20787 */
20790 (Node *) partConstraint);
20791
20792 /* XXX this sure looks wrong */
20794
20795 /*
20796 * Adjust the generated constraint to match this partition's attribute
20797 * numbers.
20798 */
20800 rel);
20801
20802 /* Validate partition constraints against the table being attached. */
20804 false);
20805 }
20806
20807 /*
20808 * If we're attaching a partition other than the default partition and a
20809 * default one exists, then that partition's partition constraint changes,
20810 * so add an entry to the work queue to validate it, too. (We must not do
20811 * this when the partition being attached is the default one; we already
20812 * did it above!)
20813 */
20815 {
20818
20819 Assert(!cmd->bound->is_default);
20820
20821 /* we already hold a lock on the default partition */
20825
20826 /*
20827 * Map the Vars in the constraint expression from rel's attnos to
20828 * defaultrel's.
20829 */
20832 1, defaultrel, rel);
20834 defPartConstraint, true);
20835
20836 /* keep our lock until commit. */
20838 }
20839
20841
20842 /*
20843 * If the partition we just attached is partitioned itself, invalidate
20844 * relcache for all descendent partitions too to ensure that their
20845 * rd_partcheck expression trees are rebuilt; partitions already locked at
20846 * the beginning of this function.
20847 */
20848 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20849 {
20850 ListCell *l;
20851
20852 foreach(l, attachrel_children)
20853 {
20855 }
20856 }
20857
20858 /* keep our lock until commit */
20860
20861 return address;
20862}
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition clauses.c:2498
#define _(x)
Definition elog.c:95
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:3867
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:210
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 21881 of file tablecmds.c.

21882{
21885 Relation parentTbl;
21886 ObjectAddress address;
21887 Oid partIdxId;
21890
21891 /*
21892 * We need to obtain lock on the index 'name' to modify it, but we also
21893 * need to read its owning table's tuple descriptor -- so we need to lock
21894 * both. To avoid deadlocks, obtain lock on the table before doing so on
21895 * the index. Furthermore, we need to examine the parent table of the
21896 * partition, so lock that one too.
21897 */
21898 state.partitionOid = InvalidOid;
21899 state.parentTblOid = parentIdx->rd_index->indrelid;
21900 state.lockedParentTbl = false;
21901 partIdxId =
21904 &state);
21905 /* Not there? */
21906 if (!OidIsValid(partIdxId))
21907 ereport(ERROR,
21909 errmsg("index \"%s\" does not exist", name->relname)));
21910
21911 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21913
21914 /* we already hold locks on both tables, so this is safe: */
21915 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21916 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21917
21919
21920 /* Silently do nothing if already in the right state */
21921 currParent = partIdx->rd_rel->relispartition ?
21923 if (currParent != RelationGetRelid(parentIdx))
21924 {
21927 AttrMap *attmap;
21928 bool found;
21929 int i;
21933
21934 /*
21935 * If this partition already has an index attached, refuse the
21936 * operation.
21937 */
21939
21941 ereport(ERROR,
21943 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21945 RelationGetRelationName(parentIdx)),
21946 errdetail("Index \"%s\" is already attached to another index.",
21948
21949 /* Make sure it indexes a partition of the other index's table */
21950 partDesc = RelationGetPartitionDesc(parentTbl, true);
21951 found = false;
21952 for (i = 0; i < partDesc->nparts; i++)
21953 {
21954 if (partDesc->oids[i] == state.partitionOid)
21955 {
21956 found = true;
21957 break;
21958 }
21959 }
21960 if (!found)
21961 ereport(ERROR,
21963 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21965 RelationGetRelationName(parentIdx)),
21966 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21968 RelationGetRelationName(parentTbl))));
21969
21970 /* Ensure the indexes are compatible */
21972 parentInfo = BuildIndexInfo(parentIdx);
21974 RelationGetDescr(parentTbl),
21975 false);
21977 partIdx->rd_indcollation,
21978 parentIdx->rd_indcollation,
21979 partIdx->rd_opfamily,
21980 parentIdx->rd_opfamily,
21981 attmap))
21982 ereport(ERROR,
21984 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21986 RelationGetRelationName(parentIdx)),
21987 errdetail("The index definitions do not match.")));
21988
21989 /*
21990 * If there is a constraint in the parent, make sure there is one in
21991 * the child too.
21992 */
21994 RelationGetRelid(parentIdx));
21995
21997 {
21999 partIdxId);
22000 if (!OidIsValid(cldConstrId))
22001 ereport(ERROR,
22003 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22005 RelationGetRelationName(parentIdx)),
22006 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
22007 RelationGetRelationName(parentIdx),
22008 RelationGetRelationName(parentTbl),
22010 }
22011
22012 /*
22013 * If it's a primary key, make sure the columns in the partition are
22014 * NOT NULL.
22015 */
22016 if (parentIdx->rd_index->indisprimary)
22018
22019 /* All good -- do it */
22024
22026
22027 validatePartitionedIndex(parentIdx, parentTbl);
22028 }
22029
22030 relation_close(parentTbl, AccessShareLock);
22031 /* keep these locks till commit */
22034
22035 return address;
22036}
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:2538
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition indexcmds.c:4471
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition partition.c:53
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
Form_pg_index rd_index
Definition rel.h:192
Oid * rd_opfamily
Definition rel.h:207
Oid * rd_indcollation
Definition rel.h:217
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
const char * name

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

Referenced by ATExecCmd().

◆ ATExecChangeOwner()

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

Definition at line 16314 of file tablecmds.c.

16315{
16318 HeapTuple tuple;
16320
16321 /*
16322 * Get exclusive lock till end of transaction on the target table. Use
16323 * relation_open so that we can work on indexes and sequences.
16324 */
16325 target_rel = relation_open(relationOid, lockmode);
16326
16327 /* Get its pg_class tuple, too */
16329
16330 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16331 if (!HeapTupleIsValid(tuple))
16332 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16334
16335 /* Can we change the ownership of this tuple? */
16336 switch (tuple_class->relkind)
16337 {
16338 case RELKIND_RELATION:
16339 case RELKIND_VIEW:
16340 case RELKIND_MATVIEW:
16343 case RELKIND_PROPGRAPH:
16344 /* ok to change owner */
16345 break;
16346 case RELKIND_INDEX:
16347 if (!recursing)
16348 {
16349 /*
16350 * Because ALTER INDEX OWNER used to be allowed, and in fact
16351 * is generated by old versions of pg_dump, we give a warning
16352 * and do nothing rather than erroring out. Also, to avoid
16353 * unnecessary chatter while restoring those old dumps, say
16354 * nothing at all if the command would be a no-op anyway.
16355 */
16356 if (tuple_class->relowner != newOwnerId)
16359 errmsg("cannot change owner of index \"%s\"",
16360 NameStr(tuple_class->relname)),
16361 errhint("Change the ownership of the index's table instead.")));
16362 /* quick hack to exit via the no-op path */
16363 newOwnerId = tuple_class->relowner;
16364 }
16365 break;
16367 if (recursing)
16368 break;
16369 ereport(ERROR,
16371 errmsg("cannot change owner of index \"%s\"",
16372 NameStr(tuple_class->relname)),
16373 errhint("Change the ownership of the index's table instead.")));
16374 break;
16375 case RELKIND_SEQUENCE:
16376 if (!recursing &&
16377 tuple_class->relowner != newOwnerId)
16378 {
16379 /* if it's an owned sequence, disallow changing it by itself */
16380 Oid tableId;
16381 int32 colId;
16382
16383 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16385 ereport(ERROR,
16387 errmsg("cannot change owner of sequence \"%s\"",
16388 NameStr(tuple_class->relname)),
16389 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16390 NameStr(tuple_class->relname),
16392 }
16393 break;
16395 if (recursing)
16396 break;
16397 ereport(ERROR,
16399 errmsg("\"%s\" is a composite type",
16400 NameStr(tuple_class->relname)),
16401 /* translator: %s is an SQL ALTER command */
16402 errhint("Use %s instead.",
16403 "ALTER TYPE")));
16404 break;
16405 case RELKIND_TOASTVALUE:
16406 if (recursing)
16407 break;
16409 default:
16410 ereport(ERROR,
16412 errmsg("cannot change owner of relation \"%s\"",
16413 NameStr(tuple_class->relname)),
16415 }
16416
16417 /*
16418 * If the new owner is the same as the existing owner, consider the
16419 * command to have succeeded. This is for dump restoration purposes.
16420 */
16421 if (tuple_class->relowner != newOwnerId)
16422 {
16426 Acl *newAcl;
16428 bool isNull;
16429 HeapTuple newtuple;
16430
16431 /* skip permission checks when recursing to index or toast table */
16432 if (!recursing)
16433 {
16434 /* Superusers can always do it */
16435 if (!superuser())
16436 {
16437 Oid namespaceOid = tuple_class->relnamespace;
16439
16440 /* Otherwise, must be owner of the existing object */
16441 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16444
16445 /* Must be able to become new owner */
16447
16448 /* New owner must have CREATE privilege on namespace */
16450 ACL_CREATE);
16451 if (aclresult != ACLCHECK_OK)
16454 }
16455 }
16456
16457 memset(repl_null, false, sizeof(repl_null));
16458 memset(repl_repl, false, sizeof(repl_repl));
16459
16462
16463 /*
16464 * Determine the modified ACL for the new owner. This is only
16465 * necessary when the ACL is non-null.
16466 */
16469 &isNull);
16470 if (!isNull)
16471 {
16473 tuple_class->relowner, newOwnerId);
16474 repl_repl[Anum_pg_class_relacl - 1] = true;
16476 }
16477
16479
16480 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16481
16482 heap_freetuple(newtuple);
16483
16484 /*
16485 * We must similarly update any per-column ACLs to reflect the new
16486 * owner; for neatness reasons that's split out as a subroutine.
16487 */
16488 change_owner_fix_column_acls(relationOid,
16489 tuple_class->relowner,
16490 newOwnerId);
16491
16492 /*
16493 * Update owner dependency reference, if any. A composite type has
16494 * none, because it's tracked for the pg_type entry instead of here;
16495 * indexes and TOAST tables don't have their own entries either.
16496 */
16497 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16498 tuple_class->relkind != RELKIND_INDEX &&
16500 tuple_class->relkind != RELKIND_TOASTVALUE)
16502 newOwnerId);
16503
16504 /*
16505 * Also change the ownership of the table's row type, if it has one
16506 */
16507 if (OidIsValid(tuple_class->reltype))
16509
16510 /*
16511 * If we are operating on a table or materialized view, also change
16512 * the ownership of any indexes and sequences that belong to the
16513 * relation, as well as its toast table (if it has one).
16514 */
16515 if (tuple_class->relkind == RELKIND_RELATION ||
16517 tuple_class->relkind == RELKIND_MATVIEW ||
16518 tuple_class->relkind == RELKIND_TOASTVALUE)
16519 {
16521 ListCell *i;
16522
16523 /* Find all the indexes belonging to this relation */
16525
16526 /* For each index, recursively change its ownership */
16527 foreach(i, index_oid_list)
16528 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16529
16531 }
16532
16533 /* If it has a toast table, recurse to change its ownership */
16534 if (tuple_class->reltoastrelid != InvalidOid)
16536 true, lockmode);
16537
16538 /* If it has dependent sequences, recurse to change them too */
16539 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16540 }
16541
16543
16544 ReleaseSysCache(tuple);
16547}
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:152
#define WARNING
Definition elog.h:36
@ OBJECT_SCHEMA
int errdetail_relkind_not_supported(char relkind)
Definition pg_class.c:24
void changeDependencyOnOwner(Oid classId, Oid objectId, Oid newOwnerId)
bool superuser(void)
Definition superuser.c:47
void ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
static void change_owner_recurse_to_sequences(Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
void AlterTypeOwnerInternal(Oid typeOid, Oid newOwnerId)
Definition typecmds.c:4028

References ACL_CREATE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, aclnewowner(), AlterTypeOwnerInternal(), ATExecChangeOwner(), CatalogTupleUpdate(), change_owner_fix_column_acls(), change_owner_recurse_to_sequences(), changeDependencyOnOwner(), check_can_set_role(), DatumGetAclP, DEPENDENCY_AUTO, DEPENDENCY_INTERNAL, elog, ereport, errcode(), errdetail(), errdetail_relkind_not_supported(), errhint(), errmsg, ERROR, fb(), get_namespace_name(), get_rel_name(), get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), heap_freetuple(), heap_modify_tuple(), HeapTupleIsValid, i, InvalidOid, InvokeObjectPostAlterHook, lfirst_oid, list_free(), NameStr, NoLock, object_aclcheck(), object_ownercheck(), OBJECT_SCHEMA, ObjectIdGetDatum(), OidIsValid, pg_fallthrough, PointerGetDatum(), relation_close(), relation_open(), RelationGetDescr, RelationGetIndexList(), RelationGetRelationName, ReleaseSysCache(), RowExclusiveLock, SearchSysCache1(), sequenceIsOwned(), superuser(), SysCacheGetAttr(), HeapTupleData::t_self, table_close(), table_open(), and WARNING.

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

◆ ATExecClusterOn()

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

Definition at line 16690 of file tablecmds.c.

16691{
16692 Oid indexOid;
16693 ObjectAddress address;
16694
16695 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16696
16697 if (!OidIsValid(indexOid))
16698 ereport(ERROR,
16700 errmsg("index \"%s\" for table \"%s\" does not exist",
16702
16703 /* Check index is valid to cluster on */
16704 check_index_is_clusterable(rel, indexOid, lockmode);
16705
16706 /* And do the work */
16707 mark_index_clustered(rel, indexOid, false);
16708
16709 ObjectAddressSet(address,
16710 RelationRelationId, indexOid);
16711
16712 return address;
16713}
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition cluster.c:508
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition cluster.c:568

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

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

8215{
8216 TupleDesc tupdesc = RelationGetDescr(rel);
8218 ObjectAddress address;
8219
8220 /*
8221 * get the number of the attribute
8222 */
8225 ereport(ERROR,
8227 errmsg("column \"%s\" of relation \"%s\" does not exist",
8229
8230 /* Prevent them from altering a system attribute */
8231 if (attnum <= 0)
8232 ereport(ERROR,
8234 errmsg("cannot alter system column \"%s\"",
8235 colName)));
8236
8237 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8238 ereport(ERROR,
8240 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8242 /* translator: %s is an SQL ALTER command */
8243 newDefault ? 0 : errhint("Use %s instead.",
8244 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8245
8246 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8247 ereport(ERROR,
8249 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8251 newDefault ?
8252 /* translator: %s is an SQL ALTER command */
8253 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8254 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8255 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8256
8257 /*
8258 * Remove any old default for the column. We use RESTRICT here for
8259 * safety, but at present we do not expect anything to depend on the
8260 * default.
8261 *
8262 * We treat removing the existing default as an internal operation when it
8263 * is preparatory to adding a new default, but as a user-initiated
8264 * operation when the user asked for a drop.
8265 */
8267 newDefault != NULL);
8268
8269 if (newDefault)
8270 {
8271 /* SET DEFAULT */
8273
8275 rawEnt->attnum = attnum;
8276 rawEnt->raw_default = newDefault;
8277 rawEnt->generated = '\0';
8278
8279 /*
8280 * This function is intended for CREATE TABLE, so it processes a
8281 * _list_ of defaults, but we just do one.
8282 */
8284 false, true, false, NULL);
8285 }
8286
8288 RelationGetRelid(rel), attnum);
8289 return address;
8290}
#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 8298 of file tablecmds.c.

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

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

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

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

21721{
21722 Relation partRel;
21723 ObjectAddress address;
21725
21727
21728 /*
21729 * Wait until existing snapshots are gone. This is important if the
21730 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21731 * user could immediately run DETACH FINALIZE without actually waiting for
21732 * existing transactions. We must not complete the detach action until
21733 * all such queries are complete (otherwise we would present them with an
21734 * inconsistent view of catalogs).
21735 */
21736 WaitForOlderSnapshots(snap->xmin, false);
21737
21738 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21739
21741
21742 table_close(partRel, NoLock);
21743
21744 return address;
21745}
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 16722 of file tablecmds.c.

16723{
16724 mark_index_clustered(rel, InvalidOid, false);
16725}

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

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

14257{
14259 SysScanDesc scan;
14260 ScanKeyData skey[3];
14261 HeapTuple tuple;
14262 bool found = false;
14263
14265
14266 /*
14267 * Find and drop the target constraint
14268 */
14269 ScanKeyInit(&skey[0],
14273 ScanKeyInit(&skey[1],
14277 ScanKeyInit(&skey[2],
14282 true, NULL, 3, skey);
14283
14284 /* There can be at most one matching row */
14285 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14286 {
14287 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14288 missing_ok, lockmode);
14289 found = true;
14290 }
14291
14292 systable_endscan(scan);
14293
14294 if (!found)
14295 {
14296 if (!missing_ok)
14297 ereport(ERROR,
14299 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14301 else
14303 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14305 }
14306
14308}
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 8876 of file tablecmds.c.

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

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

8577{
8578 HeapTuple tuple;
8582 ObjectAddress address;
8583 Oid seqid;
8585 bool ispartitioned;
8586
8587 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8588 if (ispartitioned && !recurse)
8589 ereport(ERROR,
8591 errmsg("cannot drop identity from a column of only the partitioned table"),
8592 errhint("Do not specify the ONLY keyword.")));
8593
8594 if (rel->rd_rel->relispartition && !recursing)
8595 ereport(ERROR,
8597 errmsg("cannot drop identity from a column of a partition"));
8598
8601 if (!HeapTupleIsValid(tuple))
8602 ereport(ERROR,
8604 errmsg("column \"%s\" of relation \"%s\" does not exist",
8606
8608 attnum = attTup->attnum;
8609
8610 if (attnum <= 0)
8611 ereport(ERROR,
8613 errmsg("cannot alter system column \"%s\"",
8614 colName)));
8615
8616 if (!attTup->attidentity)
8617 {
8618 if (!missing_ok)
8619 ereport(ERROR,
8621 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8623 else
8624 {
8626 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8628 heap_freetuple(tuple);
8630 return InvalidObjectAddress;
8631 }
8632 }
8633
8634 attTup->attidentity = '\0';
8635 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8636
8638 RelationGetRelid(rel),
8639 attTup->attnum);
8641 RelationGetRelid(rel), attnum);
8642 heap_freetuple(tuple);
8643
8645
8646 /*
8647 * Recurse to drop the identity from column in partitions. Identity is
8648 * not inherited in regular inheritance children so ignore them.
8649 */
8650 if (recurse && ispartitioned)
8651 {
8652 List *children;
8653 ListCell *lc;
8654
8655 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8656
8657 foreach(lc, children)
8658 {
8660
8662 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8664 }
8665 }
8666
8667 if (!recursing)
8668 {
8669 /* drop the internal sequence */
8670 seqid = getIdentitySequence(rel, attnum, false);
8675 seqaddress.objectId = seqid;
8676 seqaddress.objectSubId = 0;
8678 }
8679
8680 return address;
8681}
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 18064 of file tablecmds.c.

18065{
18066 ObjectAddress address;
18068
18069 /*
18070 * AccessShareLock on the parent is probably enough, seeing that DROP
18071 * TABLE doesn't lock parent tables at all. We need some lock since we'll
18072 * be inspecting the parent's schema.
18073 */
18075
18076 /*
18077 * We don't bother to check ownership of the parent table --- ownership of
18078 * the child is presumed enough rights.
18079 */
18080
18081 /* Off to RemoveInheritance() where most of the work happens */
18082 RemoveInheritance(rel, parent_rel, false);
18083
18086
18087 /* keep our lock on the parent relation until commit */
18089
18090 return address;
18091}

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

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

18593{
18594 Oid relid = RelationGetRelid(rel);
18596 HeapTuple tuple;
18597
18598 if (!OidIsValid(rel->rd_rel->reloftype))
18599 ereport(ERROR,
18601 errmsg("\"%s\" is not a typed table",
18603
18604 /*
18605 * We don't bother to check ownership of the type --- ownership of the
18606 * table is presumed enough rights. No lock required on the type, either.
18607 */
18608
18609 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18611
18612 /* Clear pg_class.reloftype */
18615 if (!HeapTupleIsValid(tuple))
18616 elog(ERROR, "cache lookup failed for relation %u", relid);
18617 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18619
18621
18622 heap_freetuple(tuple);
18624}

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

17467{
17468 EnableDisableRule(rel, rulename, fires_when);
17469
17471 RelationGetRelid(rel), 0);
17472}
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 17447 of file tablecmds.c.

17450{
17451 EnableDisableTrigger(rel, trigname, InvalidOid,
17452 fires_when, skip_system, recurse,
17453 lockmode);
17454
17456 RelationGetRelid(rel), 0);
17457}
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 18868 of file tablecmds.c.

18869{
18871 Oid relid;
18872 HeapTuple tuple;
18873
18874 relid = RelationGetRelid(rel);
18875
18877
18879
18880 if (!HeapTupleIsValid(tuple))
18881 elog(ERROR, "cache lookup failed for relation %u", relid);
18882
18884 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18885
18887 RelationGetRelid(rel), 0);
18888
18890 heap_freetuple(tuple);
18891}

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

18898{
18900 ForeignServer *server;
18902 HeapTuple tuple;
18903 bool isnull;
18907 Datum datum;
18909
18910 if (options == NIL)
18911 return;
18912
18914
18916 ObjectIdGetDatum(rel->rd_id));
18917 if (!HeapTupleIsValid(tuple))
18918 ereport(ERROR,
18920 errmsg("foreign table \"%s\" does not exist",
18923 server = GetForeignServer(tableform->ftserver);
18924 fdw = GetForeignDataWrapper(server->fdwid);
18925
18926 memset(repl_val, 0, sizeof(repl_val));
18927 memset(repl_null, false, sizeof(repl_null));
18928 memset(repl_repl, false, sizeof(repl_repl));
18929
18930 /* Extract the current options */
18932 tuple,
18934 &isnull);
18935 if (isnull)
18936 datum = PointerGetDatum(NULL);
18937
18938 /* Transform the options */
18940 datum,
18941 options,
18942 fdw->fdwvalidator);
18943
18944 if (DatumGetPointer(datum) != NULL)
18946 else
18948
18950
18951 /* Everything looks good - update the tuple */
18952
18955
18956 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18957
18958 /*
18959 * Invalidate relcache so that all sessions will refresh any cached plans
18960 * that might depend on the old options.
18961 */
18963
18965 RelationGetRelid(rel), 0);
18966
18968
18969 heap_freetuple(tuple);
18970}

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

23004{
23009 Oid ownerId = InvalidOid;
23010 Oid save_userid;
23011 int save_sec_context;
23012 int save_nestlevel;
23013
23014 /*
23015 * Check ownership of merged partitions - partitions with different owners
23016 * cannot be merged. Also, collect the OIDs of these partitions during the
23017 * check.
23018 */
23020 {
23022
23023 /*
23024 * We are going to detach and remove this partition. We already took
23025 * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
23026 * NoLock is fine.
23027 */
23030
23031 if (OidIsValid(ownerId))
23032 {
23033 /* Do the partitions being merged have different owners? */
23034 if (ownerId != mergingPartition->rd_rel->relowner)
23035 ereport(ERROR,
23037 errmsg("partitions being merged have different owners"));
23038 }
23039 else
23040 ownerId = mergingPartition->rd_rel->relowner;
23041
23042 /* Store the next merging partition into the list. */
23045
23047 }
23048
23049 /* Look up the existing relation by the new partition name. */
23051
23052 /*
23053 * Check if this name is already taken. This helps us to detect the
23054 * situation when one of the merging partitions has the same name as the
23055 * new partition. Otherwise, this would fail later on anyway, but
23056 * catching this here allows us to emit a nicer error message.
23057 */
23059 {
23061 {
23062 /*
23063 * The new partition has the same name as one of the merging
23064 * partitions.
23065 */
23066 char tmpRelName[NAMEDATALEN];
23067
23068 /* Generate a temporary name. */
23069 sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23070
23071 /*
23072 * Rename the existing partition with a temporary name, leaving it
23073 * free for the new partition. We don't need to care about this
23074 * in the future because we're going to eventually drop the
23075 * existing partition anyway.
23076 */
23078
23079 /*
23080 * We must bump the command counter to make the new partition
23081 * tuple visible for rename.
23082 */
23084 }
23085 else
23086 {
23087 ereport(ERROR,
23089 errmsg("relation \"%s\" already exists", cmd->name->relname));
23090 }
23091 }
23092
23095
23096 /* Detach all merging partitions. */
23098 {
23100
23102
23104
23106 }
23107
23108 /*
23109 * Perform a preliminary check to determine whether it's safe to drop all
23110 * merging partitions before we actually do so later. After merging rows
23111 * into the new partitions via MergePartitionsMoveRows, all old partitions
23112 * need to be dropped. However, since the drop behavior is DROP_RESTRICT
23113 * and the merge process (MergePartitionsMoveRows) can be time-consuming,
23114 * performing an early check on the drop eligibility of old partitions is
23115 * preferable.
23116 */
23118 {
23119 ObjectAddress object;
23120
23121 /* Get oid of the later to be dropped relation. */
23123 object.classId = RelationRelationId;
23124 object.objectSubId = 0;
23125
23127 }
23128
23129 /*
23130 * Create a table for the new partition, using the partitioned table as a
23131 * model.
23132 */
23133 Assert(OidIsValid(ownerId));
23134 newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
23135
23136 /*
23137 * Switch to the table owner's userid, so that any index functions are run
23138 * as that user. Also, lockdown security-restricted operations and
23139 * arrange to make GUC variable changes local to this command.
23140 *
23141 * Need to do it after determining the namespace in the
23142 * createPartitionTable() call.
23143 */
23144 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23145 SetUserIdAndSecContext(ownerId,
23146 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23147 save_nestlevel = NewGUCNestLevel();
23149
23150 /* Copy data from merged partitions to the new partition. */
23152
23153 /* Drop the current partitions before attaching the new one. */
23155 {
23156 ObjectAddress object;
23157
23159 object.classId = RelationRelationId;
23160 object.objectSubId = 0;
23161
23162 performDeletion(&object, DROP_RESTRICT, 0);
23163 }
23164
23166
23167 /*
23168 * Attach a new partition to the partitioned table. wqueue = NULL:
23169 * verification for each cloned constraint is not needed.
23170 */
23172
23173 /* Keep the lock until commit. */
23175
23176 /* Roll back any GUC changes executed by index functions. */
23177 AtEOXact_GUC(false, save_nestlevel);
23178
23179 /* Restore the userid and security context. */
23180 SetUserIdAndSecContext(save_userid, save_sec_context);
23181}
void performDeletionCheck(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition dependency.c:338
int MyProcPid
Definition globals.c:47
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:319
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 Relation createPartitionTable(List **wqueue, RangeVar *newPartName, Relation parent_rel, Oid ownerId)
static void detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)

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

Referenced by ATExecCmd().

◆ ATExecReplicaIdentity()

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

Definition at line 18724 of file tablecmds.c.

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

16769{
16772 HeapTuple tuple;
16773 Form_pg_class rd_rel;
16774 Oid reloid = RelationGetRelid(rel);
16775
16776 /*
16777 * Shouldn't be called on relations having storage; these are processed in
16778 * phase 3.
16779 */
16780 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
16781
16782 /* Get a modifiable copy of the relation's pg_class row. */
16784
16786 if (!HeapTupleIsValid(tuple))
16787 elog(ERROR, "cache lookup failed for relation %u", reloid);
16788 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
16789
16790 /* Update the pg_class row. */
16791 oldAccessMethodId = rd_rel->relam;
16792 rd_rel->relam = newAccessMethodId;
16793
16794 /* Leave if no update required */
16795 if (rd_rel->relam == oldAccessMethodId)
16796 {
16797 heap_freetuple(tuple);
16799 return;
16800 }
16801
16802 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
16803
16804 /*
16805 * Update the dependency on the new access method. No dependency is added
16806 * if the new access method is InvalidOid (default case). Be very careful
16807 * that this has to compare the previous value stored in pg_class with the
16808 * new one.
16809 */
16810 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
16811 {
16813 referenced;
16814
16815 /*
16816 * New access method is defined and there was no dependency
16817 * previously, so record a new one.
16818 */
16822 }
16823 else if (OidIsValid(oldAccessMethodId) &&
16824 !OidIsValid(rd_rel->relam))
16825 {
16826 /*
16827 * There was an access method defined, and no new one, so just remove
16828 * the existing dependency.
16829 */
16833 }
16834 else
16835 {
16837 OidIsValid(rd_rel->relam));
16838
16839 /* Both are valid, so update the dependency */
16842 oldAccessMethodId, rd_rel->relam);
16843 }
16844
16845 /* make the relam and dependency changes visible */
16847
16849
16850 heap_freetuple(tuple);
16852}

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

18982{
18984 HeapTuple tuple;
18987 char *compression;
18988 char cmethod;
18989 ObjectAddress address;
18990
18991 compression = strVal(newValue);
18992
18994
18995 /* copy the cache entry so we can scribble on it below */
18997 if (!HeapTupleIsValid(tuple))
18998 ereport(ERROR,
19000 errmsg("column \"%s\" of relation \"%s\" does not exist",
19002
19003 /* prevent them from altering a system attribute */
19005 attnum = atttableform->attnum;
19006 if (attnum <= 0)
19007 ereport(ERROR,
19009 errmsg("cannot alter system column \"%s\"", column)));
19010
19011 /*
19012 * Check that column type is compressible, then get the attribute
19013 * compression method code
19014 */
19015 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
19016
19017 /* update pg_attribute entry */
19018 atttableform->attcompression = cmethod;
19019 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
19020
19022 RelationGetRelid(rel),
19023 attnum);
19024
19025 /*
19026 * Apply the change to indexes as well (only for simple index columns,
19027 * matching behavior of index.c ConstructTupleDescriptor()).
19028 */
19030 false, 0,
19031 true, cmethod,
19032 lockmode);
19033
19034 heap_freetuple(tuple);
19035
19037
19038 /* make changes visible */
19040
19042 RelationGetRelid(rel), attnum);
19043 return address;
19044}
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:9204

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

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

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

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

9127{
9129 HeapTuple tuple,
9130 newtuple;
9133 Datum datum,
9134 newOptions;
9135 bool isnull;
9136 ObjectAddress address;
9140
9142
9144
9145 if (!HeapTupleIsValid(tuple))
9146 ereport(ERROR,
9148 errmsg("column \"%s\" of relation \"%s\" does not exist",
9151
9152 attnum = attrtuple->attnum;
9153 if (attnum <= 0)
9154 ereport(ERROR,
9156 errmsg("cannot alter system column \"%s\"",
9157 colName)));
9158
9159 /* Generate new proposed attoptions (text array) */
9161 &isnull);
9162 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9164 false, isReset);
9165 /* Validate new options */
9167
9168 /* Build new tuple. */
9169 memset(repl_null, false, sizeof(repl_null));
9170 memset(repl_repl, false, sizeof(repl_repl));
9171 if (newOptions != (Datum) 0)
9173 else
9178
9179 /* Update system catalog. */
9180 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9181
9183 RelationGetRelid(rel),
9184 attrtuple->attnum);
9186 RelationGetRelid(rel), attnum);
9187
9188 heap_freetuple(newtuple);
9189
9190 ReleaseSysCache(tuple);
9191
9193
9194 return address;
9195}
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 16888 of file tablecmds.c.

16890{
16891 Oid relid;
16893 HeapTuple tuple;
16894 HeapTuple newtuple;
16895 Datum datum;
16900 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
16901
16902 if (defList == NIL && operation != AT_ReplaceRelOptions)
16903 return; /* nothing to do */
16904
16906
16907 /* Fetch heap tuple */
16908 relid = RelationGetRelid(rel);
16910 if (!HeapTupleIsValid(tuple))
16911 elog(ERROR, "cache lookup failed for relation %u", relid);
16912
16913 if (operation == AT_ReplaceRelOptions)
16914 {
16915 /*
16916 * If we're supposed to replace the reloptions list, we just pretend
16917 * there were none before.
16918 */
16919 datum = (Datum) 0;
16920 }
16921 else
16922 {
16923 bool isnull;
16924
16925 /* Get the old reloptions */
16927 &isnull);
16928 if (isnull)
16929 datum = (Datum) 0;
16930 }
16931
16932 /* Generate new proposed reloptions (text array) */
16934 operation == AT_ResetRelOptions);
16935
16936 /* Validate */
16937 switch (rel->rd_rel->relkind)
16938 {
16939 case RELKIND_RELATION:
16940 case RELKIND_MATVIEW:
16941 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
16942 break;
16945 break;
16946 case RELKIND_VIEW:
16948 break;
16949 case RELKIND_INDEX:
16952 break;
16953 case RELKIND_TOASTVALUE:
16954 /* fall through to error -- shouldn't ever get here */
16955 default:
16956 ereport(ERROR,
16958 errmsg("cannot set options for relation \"%s\"",
16960 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
16961 break;
16962 }
16963
16964 /* Special-case validation of view options */
16965 if (rel->rd_rel->relkind == RELKIND_VIEW)
16966 {
16969 ListCell *cell;
16970 bool check_option = false;
16971
16972 foreach(cell, view_options)
16973 {
16974 DefElem *defel = (DefElem *) lfirst(cell);
16975
16976 if (strcmp(defel->defname, "check_option") == 0)
16977 check_option = true;
16978 }
16979
16980 /*
16981 * If the check option is specified, look to see if the view is
16982 * actually auto-updatable or not.
16983 */
16984 if (check_option)
16985 {
16986 const char *view_updatable_error =
16988
16990 ereport(ERROR,
16992 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
16993 errhint("%s", _(view_updatable_error))));
16994 }
16995 }
16996
16997 /*
16998 * All we need do here is update the pg_class row; the new options will be
16999 * propagated into relcaches during post-commit cache inval.
17000 */
17001 memset(repl_val, 0, sizeof(repl_val));
17002 memset(repl_null, false, sizeof(repl_null));
17003 memset(repl_repl, false, sizeof(repl_repl));
17004
17005 if (newOptions != (Datum) 0)
17007 else
17009
17011
17012 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17014
17015 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17017
17019
17020 heap_freetuple(newtuple);
17021
17022 ReleaseSysCache(tuple);
17023
17024 /* repeat the whole exercise for the toast table, if there's one */
17025 if (OidIsValid(rel->rd_rel->reltoastrelid))
17026 {
17028 Oid toastid = rel->rd_rel->reltoastrelid;
17029
17030 toastrel = table_open(toastid, lockmode);
17031
17032 /* Fetch heap tuple */
17034 if (!HeapTupleIsValid(tuple))
17035 elog(ERROR, "cache lookup failed for relation %u", toastid);
17036
17037 if (operation == AT_ReplaceRelOptions)
17038 {
17039 /*
17040 * If we're supposed to replace the reloptions list, we just
17041 * pretend there were none before.
17042 */
17043 datum = (Datum) 0;
17044 }
17045 else
17046 {
17047 bool isnull;
17048
17049 /* Get the old reloptions */
17051 &isnull);
17052 if (isnull)
17053 datum = (Datum) 0;
17054 }
17055
17057 false, operation == AT_ResetRelOptions);
17058
17060
17061 memset(repl_val, 0, sizeof(repl_val));
17062 memset(repl_null, false, sizeof(repl_null));
17063 memset(repl_repl, false, sizeof(repl_repl));
17064
17065 if (newOptions != (Datum) 0)
17067 else
17069
17071
17072 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17074
17075 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17076
17079 InvalidOid, true);
17080
17081 heap_freetuple(newtuple);
17082
17083 ReleaseSysCache(tuple);
17084
17086 }
17087
17089}
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
List * untransformRelOptions(Datum options)
bytea * view_reloptions(Datum reloptions, bool validate)
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
#define HEAP_RELOPT_NAMESPACES
Definition reloptions.h:62
Query * get_view_query(Relation view)
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
amoptions_function amoptions
Definition amapi.h:304
HeapTuple SearchSysCacheLocked1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:282

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

Referenced by ATExecCmd().

◆ ATExecSetRowSecurity()

static void ATExecSetRowSecurity ( Relation  rel,
bool  rls 
)
static

Definition at line 18838 of file tablecmds.c.

18839{
18841 Oid relid;
18842 HeapTuple tuple;
18843
18844 relid = RelationGetRelid(rel);
18845
18846 /* Pull the record for this relation and update it */
18848
18850
18851 if (!HeapTupleIsValid(tuple))
18852 elog(ERROR, "cache lookup failed for relation %u", relid);
18853
18855 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18856
18858 RelationGetRelid(rel), 0);
18859
18861 heap_freetuple(tuple);
18862}

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

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

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

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

17190{
17191 /*
17192 * Shouldn't be called on relations having storage; these are processed in
17193 * phase 3.
17194 */
17195 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
17196
17197 /* check if relation can be moved to its new tablespace */
17198 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
17199 {
17201 RelationGetRelid(rel),
17202 0);
17203 return;
17204 }
17205
17206 /* Update can be done, so change reltablespace */
17207 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
17208
17210
17211 /* Make sure the reltablespace change is visible */
17213}

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

23458{
23462 *listptr2;
23463 bool isSameName = false;
23464 char tmpRelName[NAMEDATALEN];
23465 List *newPartRels = NIL;
23466 ObjectAddress object;
23468 Oid save_userid;
23469 int save_sec_context;
23470 int save_nestlevel;
23471
23473
23474 /*
23475 * Partition is already locked in the transformPartitionCmdForSplit
23476 * function.
23477 */
23479
23481
23482 /* Check descriptions of new partitions. */
23484 {
23486
23487 /* Look up the existing relation by the new partition name. */
23489
23490 /*
23491 * This would fail later on anyway if the relation already exists. But
23492 * by catching it here, we can emit a nicer error message.
23493 */
23495 /* One new partition can have the same name as a split partition. */
23496 isSameName = true;
23497 else if (OidIsValid(existingRelid))
23498 ereport(ERROR,
23500 errmsg("relation \"%s\" already exists", sps->name->relname));
23501 }
23502
23503 /* Detach the split partition. */
23505
23506 /*
23507 * Perform a preliminary check to determine whether it's safe to drop the
23508 * split partition before we actually do so later. After merging rows into
23509 * the new partitions via SplitPartitionMoveRows, all old partitions need
23510 * to be dropped. However, since the drop behavior is DROP_RESTRICT and
23511 * the merge process (SplitPartitionMoveRows) can be time-consuming,
23512 * performing an early check on the drop eligibility of old partitions is
23513 * preferable.
23514 */
23515 object.objectId = splitRelOid;
23516 object.classId = RelationRelationId;
23517 object.objectSubId = 0;
23519
23520 /*
23521 * If a new partition has the same name as the split partition, then we
23522 * should rename the split partition to reuse its name.
23523 */
23524 if (isSameName)
23525 {
23526 /*
23527 * We must bump the command counter to make the split partition tuple
23528 * visible for renaming.
23529 */
23531 /* Rename partition. */
23532 sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23534
23535 /*
23536 * We must bump the command counter to make the split partition tuple
23537 * visible after renaming.
23538 */
23540 }
23541
23542 /* Create new partitions (like a split partition), without indexes. */
23544 {
23546
23548 splitRel->rd_rel->relowner);
23550 }
23551
23552 /*
23553 * Switch to the table owner's userid, so that any index functions are run
23554 * as that user. Also, lockdown security-restricted operations and
23555 * arrange to make GUC variable changes local to this command.
23556 *
23557 * Need to do it after determining the namespace in the
23558 * createPartitionTable() call.
23559 */
23560 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23561 SetUserIdAndSecContext(splitRel->rd_rel->relowner,
23562 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23563 save_nestlevel = NewGUCNestLevel();
23565
23566 /* Copy data from the split partition to the new partitions. */
23568 /* Keep the lock until commit. */
23570
23571 /* Attach new partitions to the partitioned table. */
23573 {
23576
23577 /*
23578 * wqueue = NULL: verification for each cloned constraint is not
23579 * needed.
23580 */
23581 attachPartitionTable(NULL, rel, newPartRel, sps->bound);
23582 /* Keep the lock until commit. */
23584 }
23585
23586 /* Drop the split partition. */
23587 object.classId = RelationRelationId;
23588 object.objectId = splitRelOid;
23589 object.objectSubId = 0;
23590 /* Probably DROP_CASCADE is not needed. */
23591 performDeletion(&object, DROP_RESTRICT, 0);
23592
23593 /* Roll back any GUC changes executed by index functions. */
23594 AtEOXact_GUC(false, save_nestlevel);
23595
23596 /* Restore the userid and security context. */
23597 SetUserIdAndSecContext(save_userid, save_sec_context);
23598}
struct RelationData * Relation
Definition genam.h:30
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:518
static void SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel, List *partlist, List *newPartRels)

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

Referenced by ATExecCmd().

◆ ATExecValidateConstraint()

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

Definition at line 13150 of file tablecmds.c.

13152{
13154 SysScanDesc scan;
13155 ScanKeyData skey[3];
13156 HeapTuple tuple;
13158 ObjectAddress address;
13159
13161
13162 /*
13163 * Find and check the target constraint
13164 */
13165 ScanKeyInit(&skey[0],
13169 ScanKeyInit(&skey[1],
13173 ScanKeyInit(&skey[2],
13178 true, NULL, 3, skey);
13179
13180 /* There can be at most one matching row */
13181 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
13182 ereport(ERROR,
13184 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13186
13187 con = (Form_pg_constraint) GETSTRUCT(tuple);
13188 if (con->contype != CONSTRAINT_FOREIGN &&
13189 con->contype != CONSTRAINT_CHECK &&
13190 con->contype != CONSTRAINT_NOTNULL)
13191 ereport(ERROR,
13193 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
13195 errdetail("This operation is not supported for this type of constraint."));
13196
13197 if (!con->conenforced)
13198 ereport(ERROR,
13200 errmsg("cannot validate NOT ENFORCED constraint")));
13201
13202 if (!con->convalidated)
13203 {
13204 if (con->contype == CONSTRAINT_FOREIGN)
13205 {
13206 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
13207 tuple, lockmode);
13208 }
13209 else if (con->contype == CONSTRAINT_CHECK)
13210 {
13212 tuple, recurse, recursing, lockmode);
13213 }
13214 else if (con->contype == CONSTRAINT_NOTNULL)
13215 {
13217 tuple, recurse, recursing, lockmode);
13218 }
13219
13220 ObjectAddressSet(address, ConstraintRelationId, con->oid);
13221 }
13222 else
13223 address = InvalidObjectAddress; /* already validated */
13224
13225 systable_endscan(scan);
13226
13228
13229 return address;
13230}
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 6620 of file tablecmds.c.

6621{
6622 Oid relid = RelationGetRelid(rel);
6623 AlteredTableInfo *tab;
6624 ListCell *ltab;
6625
6626 foreach(ltab, *wqueue)
6627 {
6628 tab = (AlteredTableInfo *) lfirst(ltab);
6629 if (tab->relid == relid)
6630 return tab;
6631 }
6632
6633 /*
6634 * Not there, so add it. Note that we make a copy of the relation's
6635 * existing descriptor before anything interesting can happen to it.
6636 */
6638 tab->relid = relid;
6639 tab->rel = NULL; /* set later */
6640 tab->relkind = rel->rd_rel->relkind;
6643 tab->chgAccessMethod = false;
6646 tab->chgPersistence = false;
6647
6648 *wqueue = lappend(*wqueue, tab);
6649
6650 return tab;
6651}
TupleDesc CreateTupleDescCopyConstr(TupleDesc tupdesc)
Definition tupdesc.c:334

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

5772{
5776 List *afterStmts;
5777 ListCell *lc;
5778
5779 /* Gin up an AlterTableStmt with just this subcommand and this table */
5780 atstmt->relation =
5783 -1);
5784 atstmt->relation->inh = recurse;
5785 atstmt->cmds = list_make1(cmd);
5786 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5787 atstmt->missing_ok = false;
5788
5789 /* Transform the AlterTableStmt */
5791 atstmt,
5792 context->queryString,
5793 &beforeStmts,
5794 &afterStmts);
5795
5796 /* Execute any statements that should happen before these subcommand(s) */
5797 foreach(lc, beforeStmts)
5798 {
5799 Node *stmt = (Node *) lfirst(lc);
5800
5803 }
5804
5805 /* Examine the transformed subcommands and schedule them appropriately */
5806 foreach(lc, atstmt->cmds)
5807 {
5810
5811 /*
5812 * This switch need only cover the subcommand types that can be added
5813 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5814 * executing the subcommand immediately, as a substitute for the
5815 * original subcommand. (Note, however, that this does cause
5816 * AT_AddConstraint subcommands to be rescheduled into later passes,
5817 * which is important for index and foreign key constraints.)
5818 *
5819 * We assume we needn't do any phase-1 checks for added subcommands.
5820 */
5821 switch (cmd2->subtype)
5822 {
5823 case AT_AddIndex:
5825 break;
5828 break;
5829 case AT_AddConstraint:
5830 /* Recursion occurs during execution phase */
5831 if (recurse)
5832 cmd2->recurse = true;
5833 switch (castNode(Constraint, cmd2->def)->contype)
5834 {
5835 case CONSTR_NOTNULL:
5837 break;
5838 case CONSTR_PRIMARY:
5839 case CONSTR_UNIQUE:
5840 case CONSTR_EXCLUSION:
5842 break;
5843 default:
5845 break;
5846 }
5847 break;
5849 /* This command never recurses */
5850 /* No command-specific prep needed */
5852 break;
5853 default:
5854 pass = cur_pass;
5855 break;
5856 }
5857
5858 if (pass < cur_pass)
5859 {
5860 /* Cannot schedule into a pass we already finished */
5861 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5862 pass);
5863 }
5864 else if (pass > cur_pass)
5865 {
5866 /* OK, queue it up for later */
5867 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5868 }
5869 else
5870 {
5871 /*
5872 * We should see at most one subcommand for the current pass,
5873 * which is the transformed version of the original subcommand.
5874 */
5875 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5876 {
5877 /* Found the transformed version of our subcommand */
5878 newcmd = cmd2;
5879 }
5880 else
5881 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5882 pass);
5883 }
5884 }
5885
5886 /* Queue up any after-statements to happen at the end */
5887 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5888
5889 return newcmd;
5890}
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:187
void ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context)
Definition utility.c:1970

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

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

15872{
15876 Relation rel;
15877
15878 /*
15879 * We expect that we will get only ALTER TABLE and CREATE INDEX
15880 * statements. Hence, there is no need to pass them through
15881 * parse_analyze_*() or the rewriter, but instead we need to pass them
15882 * through parse_utilcmd.c to make them ready for execution.
15883 */
15887 {
15889 Node *stmt = rs->stmt;
15890
15891 if (IsA(stmt, IndexStmt))
15894 (IndexStmt *) stmt,
15895 cmd));
15896 else if (IsA(stmt, AlterTableStmt))
15897 {
15899 List *afterStmts;
15900
15902 (AlterTableStmt *) stmt,
15903 cmd,
15904 &beforeStmts,
15905 &afterStmts);
15909 }
15910 else if (IsA(stmt, CreateStatsStmt))
15914 cmd));
15915 else
15917 }
15918
15919 /* Caller should already have acquired whatever lock we need. */
15921
15922 /*
15923 * Attach each generated command to the proper place in the work queue.
15924 * Note this could result in creation of entirely new work-queue entries.
15925 *
15926 * Also note that we have to tweak the command subtypes, because it turns
15927 * out that re-creation of indexes and constraints has to act a bit
15928 * differently from initial creation.
15929 */
15930 foreach(list_item, querytree_list)
15931 {
15932 Node *stm = (Node *) lfirst(list_item);
15933 AlteredTableInfo *tab;
15934
15935 tab = ATGetQueueEntry(wqueue, rel);
15936
15937 if (IsA(stm, IndexStmt))
15938 {
15939 IndexStmt *stmt = (IndexStmt *) stm;
15941
15942 if (!rewrite)
15944 stmt->reset_default_tblspc = true;
15945 /* keep the index's comment */
15946 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
15947
15949 newcmd->subtype = AT_ReAddIndex;
15950 newcmd->def = (Node *) stmt;
15953 }
15954 else if (IsA(stm, AlterTableStmt))
15955 {
15957 ListCell *lcmd;
15958
15959 foreach(lcmd, stmt->cmds)
15960 {
15962
15963 if (cmd->subtype == AT_AddIndex)
15964 {
15966 Oid indoid;
15967
15968 indstmt = castNode(IndexStmt, cmd->def);
15969 indoid = get_constraint_index(oldId);
15970
15971 if (!rewrite)
15972 TryReuseIndex(indoid, indstmt);
15973 /* keep any comment on the index */
15974 indstmt->idxcomment = GetComment(indoid,
15976 indstmt->reset_default_tblspc = true;
15977
15978 cmd->subtype = AT_ReAddIndex;
15980 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
15981
15982 /* recreate any comment on the constraint */
15985 oldId,
15986 rel,
15987 NIL,
15988 indstmt->idxname);
15989 }
15990 else if (cmd->subtype == AT_AddConstraint)
15991 {
15992 Constraint *con = castNode(Constraint, cmd->def);
15993
15995 /* rewriting neither side of a FK */
15996 if (con->contype == CONSTR_FOREIGN &&
15997 !rewrite && tab->rewrite == 0)
15999 con->reset_default_tblspc = true;
16003
16004 /*
16005 * Recreate any comment on the constraint. If we have
16006 * recreated a primary key, then transformTableConstraint
16007 * has added an unnamed not-null constraint here; skip
16008 * this in that case.
16009 */
16010 if (con->conname)
16013 oldId,
16014 rel,
16015 NIL,
16016 con->conname);
16017 else
16018 Assert(con->contype == CONSTR_NOTNULL);
16019 }
16020 else
16021 elog(ERROR, "unexpected statement subtype: %d",
16022 (int) cmd->subtype);
16023 }
16024 }
16025 else if (IsA(stm, AlterDomainStmt))
16026 {
16028
16029 if (stmt->subtype == AD_AddConstraint)
16030 {
16031 Constraint *con = castNode(Constraint, stmt->def);
16033
16035 cmd->def = (Node *) stmt;
16038
16039 /* recreate any comment on the constraint */
16042 oldId,
16043 NULL,
16044 stmt->typeName,
16045 con->conname);
16046 }
16047 else
16048 elog(ERROR, "unexpected statement subtype: %d",
16049 (int) stmt->subtype);
16050 }
16051 else if (IsA(stm, CreateStatsStmt))
16052 {
16055
16056 /* keep the statistics object's comment */
16057 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
16058
16060 newcmd->subtype = AT_ReAddStatistics;
16061 newcmd->def = (Node *) stmt;
16062 tab->subcmds[AT_PASS_MISC] =
16064 }
16065 else
16066 elog(ERROR, "unexpected statement type: %d",
16067 (int) nodeTag(stm));
16068 }
16069
16070 relation_close(rel, NoLock);
16071}
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:1259
#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 7255 of file tablecmds.c.

7258{
7259 if (rel->rd_rel->reloftype && !recursing)
7260 ereport(ERROR,
7262 errmsg("cannot add column to typed table")));
7263
7264 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7265 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7266
7267 if (recurse && !is_view)
7268 cmd->recurse = true;
7269}
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:6953

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

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

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

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

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

17481{
17482 if (child_rel->rd_rel->reloftype)
17483 ereport(ERROR,
17485 errmsg("cannot change inheritance of typed table")));
17486
17487 if (child_rel->rd_rel->relispartition)
17488 ereport(ERROR,
17490 errmsg("cannot change inheritance of a partition")));
17491}

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

Referenced by ATPrepCmd().

◆ ATPrepChangePersistence()

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

Definition at line 19055 of file tablecmds.c.

19056{
19058 HeapTuple tuple;
19059 SysScanDesc scan;
19060 ScanKeyData skey[1];
19061
19062 /*
19063 * Disallow changing status for a temp table. Also verify whether we can
19064 * get away with doing nothing; in such cases we don't need to run the
19065 * checks below, either.
19066 */
19067 switch (rel->rd_rel->relpersistence)
19068 {
19070 ereport(ERROR,
19072 errmsg("cannot change logged status of table \"%s\" because it is temporary",
19074 errtable(rel)));
19075 break;
19077 if (toLogged)
19078 /* nothing to do */
19079 return;
19080 break;
19082 if (!toLogged)
19083 /* nothing to do */
19084 return;
19085 break;
19086 }
19087
19088 /*
19089 * Check that the table is not part of any publication when changing to
19090 * UNLOGGED, as UNLOGGED tables can't be published.
19091 */
19092 if (!toLogged &&
19094 ereport(ERROR,
19096 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
19098 errdetail("Unlogged relations cannot be replicated.")));
19099
19100 /*
19101 * Check existing foreign key constraints to preserve the invariant that
19102 * permanent tables cannot reference unlogged ones. Self-referencing
19103 * foreign keys can safely be ignored.
19104 */
19106
19107 /*
19108 * Scan conrelid if changing to permanent, else confrelid. This also
19109 * determines whether a useful index exists.
19110 */
19111 ScanKeyInit(&skey[0],
19118 true, NULL, 1, skey);
19119
19120 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19121 {
19123
19124 if (con->contype == CONSTRAINT_FOREIGN)
19125 {
19127 Relation foreignrel;
19128
19129 /* the opposite end of what we used as scankey */
19130 foreignrelid = toLogged ? con->confrelid : con->conrelid;
19131
19132 /* ignore if self-referencing */
19133 if (RelationGetRelid(rel) == foreignrelid)
19134 continue;
19135
19137
19138 if (toLogged)
19139 {
19140 if (!RelationIsPermanent(foreignrel))
19141 ereport(ERROR,
19143 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
19145 RelationGetRelationName(foreignrel)),
19146 errtableconstraint(rel, NameStr(con->conname))));
19147 }
19148 else
19149 {
19150 if (RelationIsPermanent(foreignrel))
19151 ereport(ERROR,
19153 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
19155 RelationGetRelationName(foreignrel)),
19156 errtableconstraint(rel, NameStr(con->conname))));
19157 }
19158
19159 relation_close(foreignrel, AccessShareLock);
19160 }
19161 }
19162
19163 systable_endscan(scan);
19164
19166
19167 /* force rewrite if necessary; see comment in ATRewriteTables */
19169 if (toLogged)
19171 else
19173 tab->chgPersistence = true;
19174}
#define AT_REWRITE_ALTER_PERSISTENCE
return true
Definition isn.c:130
int errtableconstraint(Relation rel, const char *conname)
Definition relcache.c:6116
int errtable(Relation rel)
Definition relcache.c:6062

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

4944{
4945 AlteredTableInfo *tab;
4947
4948 /* Find or create work queue entry for this table */
4949 tab = ATGetQueueEntry(wqueue, rel);
4950
4951 /*
4952 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4953 * partitions that are pending detach.
4954 */
4955 if (rel->rd_rel->relispartition &&
4958 ereport(ERROR,
4960 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4962 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4963
4964 /*
4965 * Copy the original subcommand for each table, so we can scribble on it.
4966 * This avoids conflicts when different child tables need to make
4967 * different parse transformations (for example, the same column may have
4968 * different column numbers in different children).
4969 */
4970 cmd = copyObject(cmd);
4971
4972 /*
4973 * Do permissions and relkind checking, recursion to child tables if
4974 * needed, and any additional phase-1 processing needed. (But beware of
4975 * adding any processing that looks at table details that another
4976 * subcommand could change. In some cases we reject multiple subcommands
4977 * that could try to change the same state in contrary ways.)
4978 */
4979 switch (cmd->subtype)
4980 {
4981 case AT_AddColumn: /* ADD COLUMN */
4982 ATSimplePermissions(cmd->subtype, rel,
4985 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
4986 lockmode, context);
4987 /* Recursion occurs during execution phase */
4989 break;
4990 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
4992 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
4993 lockmode, context);
4994 /* Recursion occurs during execution phase */
4996 break;
4997 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
4998
4999 /*
5000 * We allow defaults on views so that INSERT into a view can have
5001 * default-ish behavior. This works because the rewriter
5002 * substitutes default values into INSERTs before it expands
5003 * rules.
5004 */
5005 ATSimplePermissions(cmd->subtype, rel,
5008 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5009 /* No command-specific prep needed */
5011 break;
5012 case AT_CookedColumnDefault: /* add a pre-cooked default */
5013 /* This is currently used only in CREATE TABLE */
5014 /* (so the permission check really isn't necessary) */
5015 ATSimplePermissions(cmd->subtype, rel,
5017 /* This command never recurses */
5019 break;
5020 case AT_AddIdentity:
5021 ATSimplePermissions(cmd->subtype, rel,
5024 /* Set up recursion for phase 2; no other prep needed */
5025 if (recurse)
5026 cmd->recurse = true;
5028 break;
5029 case AT_SetIdentity:
5030 ATSimplePermissions(cmd->subtype, rel,
5033 /* Set up recursion for phase 2; no other prep needed */
5034 if (recurse)
5035 cmd->recurse = true;
5036 /* This should run after AddIdentity, so do it in MISC pass */
5038 break;
5039 case AT_DropIdentity:
5040 ATSimplePermissions(cmd->subtype, rel,
5043 /* Set up recursion for phase 2; no other prep needed */
5044 if (recurse)
5045 cmd->recurse = true;
5047 break;
5048 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5049 ATSimplePermissions(cmd->subtype, rel,
5051 /* Set up recursion for phase 2; no other prep needed */
5052 if (recurse)
5053 cmd->recurse = true;
5055 break;
5056 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5057 ATSimplePermissions(cmd->subtype, rel,
5059 /* Set up recursion for phase 2; no other prep needed */
5060 if (recurse)
5061 cmd->recurse = true;
5063 break;
5064 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5065 ATSimplePermissions(cmd->subtype, rel,
5067 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5069 break;
5070 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5071 ATSimplePermissions(cmd->subtype, rel,
5073 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5074 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5076 break;
5077 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5078 ATSimplePermissions(cmd->subtype, rel,
5081 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5082 /* No command-specific prep needed */
5084 break;
5085 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5086 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5087 ATSimplePermissions(cmd->subtype, rel,
5090 /* This command never recurses */
5092 break;
5093 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5094 ATSimplePermissions(cmd->subtype, rel,
5097 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5098 /* No command-specific prep needed */
5100 break;
5101 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5102 ATSimplePermissions(cmd->subtype, rel,
5104 /* This command never recurses */
5105 /* No command-specific prep needed */
5107 break;
5108 case AT_DropColumn: /* DROP COLUMN */
5109 ATSimplePermissions(cmd->subtype, rel,
5112 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5113 lockmode, context);
5114 /* Recursion occurs during execution phase */
5116 break;
5117 case AT_AddIndex: /* ADD INDEX */
5119 /* This command never recurses */
5120 /* No command-specific prep needed */
5122 break;
5123 case AT_AddConstraint: /* ADD CONSTRAINT */
5124 ATSimplePermissions(cmd->subtype, rel,
5126 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5127 if (recurse)
5128 {
5129 /* recurses at exec time; lock descendants and set flag */
5130 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5131 cmd->recurse = true;
5132 }
5134 break;
5135 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5137 /* This command never recurses */
5138 /* No command-specific prep needed */
5140 break;
5141 case AT_DropConstraint: /* DROP CONSTRAINT */
5142 ATSimplePermissions(cmd->subtype, rel,
5144 ATCheckPartitionsNotInUse(rel, lockmode);
5145 /* Other recursion occurs during execution phase */
5146 /* No command-specific prep needed except saving recurse flag */
5147 if (recurse)
5148 cmd->recurse = true;
5150 break;
5151 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5152 ATSimplePermissions(cmd->subtype, rel,
5155 /* See comments for ATPrepAlterColumnType */
5156 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5157 AT_PASS_UNSET, context);
5158 Assert(cmd != NULL);
5159 /* Performs own recursion */
5160 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5161 lockmode, context);
5163 break;
5166 /* This command never recurses */
5167 /* No command-specific prep needed */
5169 break;
5170 case AT_ChangeOwner: /* ALTER OWNER */
5171 /* This command never recurses */
5172 /* No command-specific prep needed */
5174 break;
5175 case AT_ClusterOn: /* CLUSTER ON */
5176 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5177 ATSimplePermissions(cmd->subtype, rel,
5179 /* These commands never recurse */
5180 /* No command-specific prep needed */
5182 break;
5183 case AT_SetLogged: /* SET LOGGED */
5184 case AT_SetUnLogged: /* SET UNLOGGED */
5186 if (tab->chgPersistence)
5187 ereport(ERROR,
5189 errmsg("cannot change persistence setting twice")));
5190 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5192 break;
5193 case AT_DropOids: /* SET WITHOUT OIDS */
5194 ATSimplePermissions(cmd->subtype, rel,
5197 break;
5198 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5199 ATSimplePermissions(cmd->subtype, rel,
5201
5202 /* check if another access method change was already requested */
5203 if (tab->chgAccessMethod)
5204 ereport(ERROR,
5206 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5207
5208 ATPrepSetAccessMethod(tab, rel, cmd->name);
5209 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5210 break;
5211 case AT_SetTableSpace: /* SET TABLESPACE */
5214 /* This command never recurses */
5215 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5216 pass = AT_PASS_MISC; /* doesn't actually matter */
5217 break;
5218 case AT_SetRelOptions: /* SET (...) */
5219 case AT_ResetRelOptions: /* RESET (...) */
5220 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5221 ATSimplePermissions(cmd->subtype, rel,
5224 /* This command never recurses */
5225 /* No command-specific prep needed */
5227 break;
5228 case AT_AddInherit: /* INHERIT */
5229 ATSimplePermissions(cmd->subtype, rel,
5231 /* This command never recurses */
5234 break;
5235 case AT_DropInherit: /* NO INHERIT */
5236 ATSimplePermissions(cmd->subtype, rel,
5238 /* This command never recurses */
5241 break;
5242 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5243 ATSimplePermissions(cmd->subtype, rel,
5245 /* Recursion occurs during execution phase */
5246 if (recurse)
5247 cmd->recurse = true;
5249 break;
5250 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5251 ATSimplePermissions(cmd->subtype, rel,
5253 /* Recursion occurs during execution phase */
5254 /* No command-specific prep needed except saving recurse flag */
5255 if (recurse)
5256 cmd->recurse = true;
5258 break;
5259 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5260 ATSimplePermissions(cmd->subtype, rel,
5263 /* This command never recurses */
5264 /* No command-specific prep needed */
5265 break;
5266 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5269 case AT_EnableTrigAll:
5270 case AT_EnableTrigUser:
5271 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5272 case AT_DisableTrigAll:
5273 case AT_DisableTrigUser:
5274 ATSimplePermissions(cmd->subtype, rel,
5276 /* Set up recursion for phase 2; no other prep needed */
5277 if (recurse)
5278 cmd->recurse = true;
5280 break;
5281 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5284 case AT_DisableRule:
5285 case AT_AddOf: /* OF */
5286 case AT_DropOf: /* NOT OF */
5291 ATSimplePermissions(cmd->subtype, rel,
5293 /* These commands never recurse */
5294 /* No command-specific prep needed */
5296 break;
5297 case AT_GenericOptions:
5299 /* No command-specific prep needed */
5301 break;
5302 case AT_AttachPartition:
5303 ATSimplePermissions(cmd->subtype, rel,
5305 /* No command-specific prep needed */
5307 break;
5308 case AT_DetachPartition:
5310 /* No command-specific prep needed */
5312 break;
5315 /* No command-specific prep needed */
5317 break;
5318 case AT_MergePartitions:
5319 case AT_SplitPartition:
5321 /* No command-specific prep needed */
5323 break;
5324 default: /* oops */
5325 elog(ERROR, "unrecognized alter table type: %d",
5326 (int) cmd->subtype);
5327 pass = AT_PASS_UNSET; /* keep compiler quiet */
5328 break;
5329 }
5331
5332 /* Add the subcommand to the appropriate list for phase 2 */
5333 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5334}
bool PartitionHasPendingDetach(Oid partoid)
static void ATPrepChangeInherit(Relation child_rel)
#define ATT_SEQUENCE
Definition tablecmds.c:343
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition tablecmds.c:6923
#define ATT_INDEX
Definition tablecmds.c:339
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition tablecmds.c:8830
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:9330
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:7255
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:6878
#define ATT_PARTITIONED_INDEX
Definition tablecmds.c:342
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:9573
#define ATT_VIEW
Definition tablecmds.c:337
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:340
#define ATT_MATVIEW
Definition tablecmds.c:338
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 9330 of file tablecmds.c.

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

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

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

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

16735{
16736 Oid amoid;
16737
16738 /*
16739 * Look up the access method name and check that it differs from the
16740 * table's current AM. If DEFAULT was specified for a partitioned table
16741 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16742 */
16743 if (amname != NULL)
16744 amoid = get_table_am_oid(amname, false);
16745 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16746 amoid = InvalidOid;
16747 else
16749
16750 /* if it's a match, phase 3 doesn't need to do anything */
16751 if (rel->rd_rel->relam == amoid)
16752 return;
16753
16754 /* Save info for Phase 3 to do the real work */
16756 tab->newAccessMethod = amoid;
16757 tab->chgAccessMethod = true;
16758}
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 16858 of file tablecmds.c.

16859{
16861
16862 /* Check that the tablespace exists */
16863 tablespaceId = get_tablespace_oid(tablespacename, false);
16864
16865 /* Check permissions except when moving to database's default */
16867 {
16869
16871 if (aclresult != ACLCHECK_OK)
16872 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
16873 }
16874
16875 /* Save info for Phase 3 to do the real work */
16876 if (OidIsValid(tab->newTableSpace))
16877 ereport(ERROR,
16879 errmsg("cannot have multiple SET TABLESPACE subcommands")));
16880
16882}

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

5346{
5347 ListCell *ltab;
5348
5349 /*
5350 * We process all the tables "in parallel", one pass at a time. This is
5351 * needed because we may have to propagate work from one table to another
5352 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5353 * re-adding of the foreign key constraint to the other table). Work can
5354 * only be propagated into later passes, however.
5355 */
5356 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5357 {
5358 /* Go through each table that needs to be processed */
5359 foreach(ltab, *wqueue)
5360 {
5362 List *subcmds = tab->subcmds[pass];
5363 ListCell *lcmd;
5364
5365 if (subcmds == NIL)
5366 continue;
5367
5368 /*
5369 * Open the relation and store it in tab. This allows subroutines
5370 * close and reopen, if necessary. Appropriate lock was obtained
5371 * by phase 1, needn't get it again.
5372 */
5373 tab->rel = relation_open(tab->relid, NoLock);
5374
5375 foreach(lcmd, subcmds)
5376 ATExecCmd(wqueue, tab,
5378 lockmode, pass, context);
5379
5380 /*
5381 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5382 * (this is not done in ATExecAlterColumnType since it should be
5383 * done only once if multiple columns of a table are altered).
5384 */
5386 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5387
5388 if (tab->rel)
5389 {
5390 relation_close(tab->rel, NoLock);
5391 tab->rel = NULL;
5392 }
5393 }
5394 }
5395
5396 /* Check to see if a toast table must be added. */
5397 foreach(ltab, *wqueue)
5398 {
5400
5401 /*
5402 * If the table is source table of ATTACH PARTITION command, we did
5403 * not modify anything about it that will change its toasting
5404 * requirement, so no need to check.
5405 */
5406 if (((tab->relkind == RELKIND_RELATION ||
5408 tab->partition_constraint == NULL) ||
5409 tab->relkind == RELKIND_MATVIEW)
5410 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5411 }
5412}
Expr * partition_constraint
Definition tablecmds.c:200
#define AT_NUM_PASSES
Definition tablecmds.c:168
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:5418
void AlterTableCreateToastTable(Oid relOid, Datum reloptions, LOCKMODE lockmode)
Definition toasting.c:58

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

Referenced by ATController().

◆ ATRewriteTable()

static void ATRewriteTable ( AlteredTableInfo tab,
Oid  OIDNewHeap 
)
static

Definition at line 6184 of file tablecmds.c.

6185{
6190 bool needscan = false;
6193 int i;
6194 ListCell *l;
6195 EState *estate;
6196 CommandId mycid;
6197 BulkInsertState bistate;
6198 int ti_options;
6199 ExprState *partqualstate = NULL;
6200
6201 /*
6202 * Open the relation(s). We have surely already locked the existing
6203 * table.
6204 */
6205 oldrel = table_open(tab->relid, NoLock);
6206 oldTupDesc = tab->oldDesc;
6207 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6208
6210 {
6212 false));
6214 }
6215 else
6216 newrel = NULL;
6217
6218 /*
6219 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6220 * is empty, so don't bother using it.
6221 */
6222 if (newrel)
6223 {
6224 mycid = GetCurrentCommandId(true);
6225 bistate = GetBulkInsertState();
6226 ti_options = TABLE_INSERT_SKIP_FSM;
6227 }
6228 else
6229 {
6230 /* keep compiler quiet about using these uninitialized */
6231 mycid = 0;
6232 bistate = NULL;
6233 ti_options = 0;
6234 }
6235
6236 /*
6237 * Generate the constraint and default execution states
6238 */
6239
6240 estate = CreateExecutorState();
6241
6242 /* Build the needed expression execution states */
6243 foreach(l, tab->constraints)
6244 {
6245 NewConstraint *con = lfirst(l);
6246
6247 switch (con->contype)
6248 {
6249 case CONSTR_CHECK:
6250 needscan = true;
6252 break;
6253 case CONSTR_FOREIGN:
6254 /* Nothing to do here */
6255 break;
6256 default:
6257 elog(ERROR, "unrecognized constraint type: %d",
6258 (int) con->contype);
6259 }
6260 }
6261
6262 /* Build expression execution states for partition check quals */
6263 if (tab->partition_constraint)
6264 {
6265 needscan = true;
6266 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6267 }
6268
6269 foreach(l, tab->newvals)
6270 {
6271 NewColumnValue *ex = lfirst(l);
6272
6273 /* expr already planned */
6274 ex->exprstate = ExecInitExpr(ex->expr, NULL);
6275 }
6276
6278 if (newrel || tab->verify_new_notnull)
6279 {
6280 /*
6281 * If we are rebuilding the tuples OR if we added any new but not
6282 * verified not-null constraints, check all *valid* not-null
6283 * constraints. This is a bit of overkill but it minimizes risk of
6284 * bugs.
6285 *
6286 * notnull_attrs does *not* collect attribute numbers for valid
6287 * not-null constraints over virtual generated columns; instead, they
6288 * are collected in notnull_virtual_attrs for verification elsewhere.
6289 */
6290 for (i = 0; i < newTupDesc->natts; i++)
6291 {
6293
6294 if (attr->attnullability == ATTNULLABLE_VALID &&
6295 !attr->attisdropped)
6296 {
6298
6299 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6301 else
6303 wholeatt->attnum);
6304 }
6305 }
6307 needscan = true;
6308 }
6309
6310 if (newrel || needscan)
6311 {
6312 ExprContext *econtext;
6315 TableScanDesc scan;
6318 ListCell *lc;
6319 Snapshot snapshot;
6321
6322 /*
6323 * When adding or changing a virtual generated column with a not-null
6324 * constraint, we need to evaluate whether the generation expression
6325 * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6326 * prepare a dummy ResultRelInfo.
6327 */
6329 {
6330 MemoryContext oldcontext;
6331
6332 Assert(newTupDesc->constr->has_generated_virtual);
6333 Assert(newTupDesc->constr->has_not_null);
6334 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6337 oldrel,
6338 0, /* dummy rangetable index */
6339 NULL,
6340 estate->es_instrument);
6341 MemoryContextSwitchTo(oldcontext);
6342 }
6343
6344 if (newrel)
6346 (errmsg_internal("rewriting table \"%s\"",
6348 else
6350 (errmsg_internal("verifying table \"%s\"",
6352
6353 if (newrel)
6354 {
6355 /*
6356 * All predicate locks on the tuples or pages are about to be made
6357 * invalid, because we move tuples around. Promote them to
6358 * relation locks.
6359 */
6361 }
6362
6363 econtext = GetPerTupleExprContext(estate);
6364
6365 /*
6366 * Create necessary tuple slots. When rewriting, two slots are needed,
6367 * otherwise one suffices. In the case where one slot suffices, we
6368 * need to use the new tuple descriptor, otherwise some constraints
6369 * can't be evaluated. Note that even when the tuple layout is the
6370 * same and no rewrite is required, the tupDescs might not be
6371 * (consider ADD COLUMN without a default).
6372 */
6373 if (tab->rewrite)
6374 {
6375 Assert(newrel != NULL);
6380
6381 /*
6382 * Set all columns in the new slot to NULL initially, to ensure
6383 * columns added as part of the rewrite are initialized to NULL.
6384 * That is necessary as tab->newvals will not contain an
6385 * expression for columns with a NULL default, e.g. when adding a
6386 * column without a default together with a column with a default
6387 * requiring an actual rewrite.
6388 */
6390 }
6391 else
6392 {
6395 newslot = NULL;
6396 }
6397
6398 /*
6399 * Any attributes that are dropped according to the new tuple
6400 * descriptor can be set to NULL. We precompute the list of dropped
6401 * attributes to avoid needing to do so in the per-tuple loop.
6402 */
6403 for (i = 0; i < newTupDesc->natts; i++)
6404 {
6405 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6407 }
6408
6409 /*
6410 * Scan through the rows, generating a new row if needed and then
6411 * checking all the constraints.
6412 */
6413 snapshot = RegisterSnapshot(GetLatestSnapshot());
6414 scan = table_beginscan(oldrel, snapshot, 0, NULL);
6415
6416 /*
6417 * Switch to per-tuple memory context and reset it for each tuple
6418 * produced, so we don't leak memory.
6419 */
6421
6423 {
6425
6426 if (tab->rewrite > 0)
6427 {
6428 /* Extract data from old tuple */
6431
6432 /* copy attributes */
6433 memcpy(newslot->tts_values, oldslot->tts_values,
6434 sizeof(Datum) * oldslot->tts_nvalid);
6435 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6436 sizeof(bool) * oldslot->tts_nvalid);
6437
6438 /* Set dropped attributes to null in new tuple */
6439 foreach(lc, dropped_attrs)
6440 newslot->tts_isnull[lfirst_int(lc)] = true;
6441
6442 /*
6443 * Constraints and GENERATED expressions might reference the
6444 * tableoid column, so fill tts_tableOid with the desired
6445 * value. (We must do this each time, because it gets
6446 * overwritten with newrel's OID during storing.)
6447 */
6448 newslot->tts_tableOid = RelationGetRelid(oldrel);
6449
6450 /*
6451 * Process supplied expressions to replace selected columns.
6452 *
6453 * First, evaluate expressions whose inputs come from the old
6454 * tuple.
6455 */
6456 econtext->ecxt_scantuple = oldslot;
6457
6458 foreach(l, tab->newvals)
6459 {
6460 NewColumnValue *ex = lfirst(l);
6461
6462 if (ex->is_generated)
6463 continue;
6464
6465 newslot->tts_values[ex->attnum - 1]
6466 = ExecEvalExpr(ex->exprstate,
6467 econtext,
6468 &newslot->tts_isnull[ex->attnum - 1]);
6469 }
6470
6472
6473 /*
6474 * Now, evaluate any expressions whose inputs come from the
6475 * new tuple. We assume these columns won't reference each
6476 * other, so that there's no ordering dependency.
6477 */
6478 econtext->ecxt_scantuple = newslot;
6479
6480 foreach(l, tab->newvals)
6481 {
6482 NewColumnValue *ex = lfirst(l);
6483
6484 if (!ex->is_generated)
6485 continue;
6486
6487 newslot->tts_values[ex->attnum - 1]
6488 = ExecEvalExpr(ex->exprstate,
6489 econtext,
6490 &newslot->tts_isnull[ex->attnum - 1]);
6491 }
6492
6494 }
6495 else
6496 {
6497 /*
6498 * If there's no rewrite, old and new table are guaranteed to
6499 * have the same AM, so we can just use the old slot to verify
6500 * new constraints etc.
6501 */
6503 }
6504
6505 /* Now check any constraints on the possibly-changed tuple */
6506 econtext->ecxt_scantuple = insertslot;
6507
6509 {
6511 {
6513
6514 ereport(ERROR,
6516 errmsg("column \"%s\" of relation \"%s\" contains null values",
6517 NameStr(attr->attname),
6520 }
6521 }
6522
6524 {
6526
6528 estate,
6531 {
6533
6534 ereport(ERROR,
6536 errmsg("column \"%s\" of relation \"%s\" contains null values",
6537 NameStr(attr->attname),
6540 }
6541 }
6542
6543 foreach(l, tab->constraints)
6544 {
6545 NewConstraint *con = lfirst(l);
6546
6547 switch (con->contype)
6548 {
6549 case CONSTR_CHECK:
6550 if (!ExecCheck(con->qualstate, econtext))
6551 ereport(ERROR,
6553 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6554 con->name,
6557 break;
6558 case CONSTR_NOTNULL:
6559 case CONSTR_FOREIGN:
6560 /* Nothing to do here */
6561 break;
6562 default:
6563 elog(ERROR, "unrecognized constraint type: %d",
6564 (int) con->contype);
6565 }
6566 }
6567
6568 if (partqualstate && !ExecCheck(partqualstate, econtext))
6569 {
6570 if (tab->validate_default)
6571 ereport(ERROR,
6573 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6575 errtable(oldrel)));
6576 else
6577 ereport(ERROR,
6579 errmsg("partition constraint of relation \"%s\" is violated by some row",
6581 errtable(oldrel)));
6582 }
6583
6584 /* Write the tuple out to the new relation */
6585 if (newrel)
6587 ti_options, bistate);
6588
6589 ResetExprContext(econtext);
6590
6592 }
6593
6595 table_endscan(scan);
6596 UnregisterSnapshot(snapshot);
6597
6599 if (newslot)
6601 }
6602
6603 FreeExecutorState(estate);
6604
6606 if (newrel)
6607 {
6608 FreeBulkInsertState(bistate);
6609
6610 table_finish_bulk_insert(newrel, ti_options);
6611
6613 }
6614}
uint32 CommandId
Definition c.h:752
int int errmsg_internal(const char *fmt,...) pg_attribute_printf(1
#define DEBUG1
Definition elog.h:30
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition execExpr.c:143
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:1262
AttrNumber ExecRelGenVirtualNotNull(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, List *notnull_virtual_attrs)
Definition execMain.c:2113
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:654
#define GetPerTupleMemoryContext(estate)
Definition executor.h:665
BulkInsertState GetBulkInsertState(void)
Definition heapam.c:2085
void FreeBulkInsertState(BulkInsertState bistate)
Definition heapam.c:2102
List * lappend_int(List *list, int datum)
Definition list.c:357
bool CheckRelationOidLockedByMe(Oid relid, LOCKMODE lockmode, bool orstronger)
Definition lmgr.c:351
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:123
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
#define foreach_int(var, lst)
Definition pg_list.h:470
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition predicate.c:3132
int errtablecol(Relation rel, int attnum)
Definition relcache.c:6079
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:732
MemoryContext es_query_cxt
Definition execnodes.h:722
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:284
ConstrType contype
Definition tablecmds.c:219
ExprState * qualstate
Definition tablecmds.c:225
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition tableam.c:59
#define TABLE_INSERT_SKIP_FSM
Definition tableam.h:259
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, int options, BulkInsertStateData *bistate)
Definition tableam.h:1388
static void table_finish_bulk_insert(Relation rel, int options)
Definition tableam.h:1585
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition tableam.h:1039
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key)
Definition tableam.h:896
#define ATTNULLABLE_VALID
Definition tupdesc.h:86
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:193
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(), MemoryContextSwitchTo(), NewConstraint::name, NameStr, AlteredTableInfo::newvals, NIL, NoLock, OidIsValid, AlteredTableInfo::oldDesc, AlteredTableInfo::partition_constraint, NewConstraint::qual, NewConstraint::qualstate, RegisterSnapshot(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, AlteredTableInfo::relid, ResetExprContext, AlteredTableInfo::rewrite, slot_attisnull(), slot_getallattrs(), table_beginscan(), table_close(), table_endscan(), table_finish_bulk_insert(), TABLE_INSERT_SKIP_FSM, table_open(), table_scan_getnextslot(), table_slot_callbacks(), table_tuple_insert(), TransferPredicateLocksToHeapRelation(), TupleDescAttr(), TupleDescCompactAttr(), UnregisterSnapshot(), AlteredTableInfo::validate_default, and AlteredTableInfo::verify_new_notnull.

Referenced by ATRewriteTables().

◆ ATRewriteTables()

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

Definition at line 5896 of file tablecmds.c.

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

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

6802{
6803 int actual_target;
6804
6805 switch (rel->rd_rel->relkind)
6806 {
6807 case RELKIND_RELATION:
6809 break;
6812 break;
6813 case RELKIND_VIEW:
6815 break;
6816 case RELKIND_MATVIEW:
6818 break;
6819 case RELKIND_INDEX:
6821 break;
6824 break;
6827 break;
6830 break;
6831 case RELKIND_SEQUENCE:
6833 break;
6834 default:
6835 actual_target = 0;
6836 break;
6837 }
6838
6839 /* Wrong target type? */
6840 if ((actual_target & allowed_targets) == 0)
6841 {
6842 const char *action_str = alter_table_type_to_string(cmdtype);
6843
6844 if (action_str)
6845 ereport(ERROR,
6847 /* translator: %s is a group of some SQL keywords */
6848 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6850 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6851 else
6852 /* internal error? */
6853 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6855 }
6856
6857 /* Permissions checks */
6861
6863 ereport(ERROR,
6865 errmsg("permission denied: \"%s\" is a system catalog",
6867}
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition tablecmds.c:6654

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

6881{
6882 /*
6883 * Propagate to children, if desired and if there are (or might be) any
6884 * children.
6885 */
6886 if (recurse && rel->rd_rel->relhassubclass)
6887 {
6888 Oid relid = RelationGetRelid(rel);
6889 ListCell *child;
6890 List *children;
6891
6892 children = find_all_inheritors(relid, lockmode, NULL);
6893
6894 /*
6895 * find_all_inheritors does the recursive search of the inheritance
6896 * hierarchy, so all we have to do is process all of the relids in the
6897 * list that it returns.
6898 */
6899 foreach(child, children)
6900 {
6901 Oid childrelid = lfirst_oid(child);
6903
6904 if (childrelid == relid)
6905 continue;
6906 /* find_all_inheritors already got lock */
6909 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6911 }
6912 }
6913}

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

20874{
20875 List *idxes;
20879 ListCell *cell;
20880 MemoryContext cxt;
20882
20884 "AttachPartitionEnsureIndexes",
20887
20892
20893 /* Build arrays of all existing indexes and their IndexInfos */
20895 {
20897
20900 }
20901
20902 /*
20903 * If we're attaching a foreign table, we must fail if any of the indexes
20904 * is a constraint index; otherwise, there's nothing to do here. Do this
20905 * before starting work, to avoid wasting the effort of building a few
20906 * non-unique indexes before coming across a unique one.
20907 */
20908 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20909 {
20910 foreach(cell, idxes)
20911 {
20912 Oid idx = lfirst_oid(cell);
20914
20915 if (idxRel->rd_index->indisunique ||
20916 idxRel->rd_index->indisprimary)
20917 ereport(ERROR,
20919 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20922 errdetail("Partitioned table \"%s\" contains unique indexes.",
20925 }
20926
20927 goto out;
20928 }
20929
20930 /*
20931 * For each index on the partitioned table, find a matching one in the
20932 * partition-to-be; if one is not found, create one.
20933 */
20934 foreach(cell, idxes)
20935 {
20936 Oid idx = lfirst_oid(cell);
20938 IndexInfo *info;
20939 AttrMap *attmap;
20940 bool found = false;
20942
20943 /*
20944 * Ignore indexes in the partitioned table other than partitioned
20945 * indexes.
20946 */
20947 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20948 {
20950 continue;
20951 }
20952
20953 /* construct an indexinfo to compare existing indexes against */
20954 info = BuildIndexInfo(idxRel);
20956 RelationGetDescr(rel),
20957 false);
20959
20960 /*
20961 * Scan the list of existing indexes in the partition-to-be, and mark
20962 * the first matching, valid, unattached one we find, if any, as
20963 * partition of the parent index. If we find one, we're done.
20964 */
20965 for (int i = 0; i < list_length(attachRelIdxs); i++)
20966 {
20969
20970 /* does this index have a parent? if so, can't use it */
20971 if (attachrelIdxRels[i]->rd_rel->relispartition)
20972 continue;
20973
20974 /* If this index is invalid, can't use it */
20975 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20976 continue;
20977
20978 if (CompareIndexInfo(attachInfos[i], info,
20979 attachrelIdxRels[i]->rd_indcollation,
20980 idxRel->rd_indcollation,
20981 attachrelIdxRels[i]->rd_opfamily,
20982 idxRel->rd_opfamily,
20983 attmap))
20984 {
20985 /*
20986 * If this index is being created in the parent because of a
20987 * constraint, then the child needs to have a constraint also,
20988 * so look for one. If there is no such constraint, this
20989 * index is no good, so keep looking.
20990 */
20992 {
20993 cldConstrOid =
20995 cldIdxId);
20996 /* no dice */
20998 continue;
20999
21000 /* Ensure they're both the same type of constraint */
21003 continue;
21004 }
21005
21006 /* bingo. */
21011 found = true;
21012
21014 break;
21015 }
21016 }
21017
21018 /*
21019 * If no suitable index was found in the partition-to-be, create one
21020 * now. Note that if this is a PK, not-null constraints must already
21021 * exist.
21022 */
21023 if (!found)
21024 {
21025 IndexStmt *stmt;
21026 Oid conOid;
21027
21029 idxRel, attmap,
21030 &conOid);
21034 conOid,
21035 -1,
21036 true, false, false, false, false);
21037 }
21038
21040 }
21041
21042out:
21043 /* Clean up. */
21044 for (int i = 0; i < list_length(attachRelIdxs); i++)
21048}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:262
char get_constraint_type(Oid conoid)
Definition lsyscache.c:1289
MemoryContext CurrentMemoryContext
Definition mcxt.c:160
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
#define foreach_current_index(var_or_cell)
Definition pg_list.h:403

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

Referenced by attachPartitionTable().

◆ AttachPartitionForeignKey()

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

Definition at line 11865 of file tablecmds.c.

11872{
11877 bool queueValidation;
11881
11882 /* Fetch the parent constraint tuple */
11886 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11888 parentConstrIsEnforced = parentConstr->conenforced;
11889
11890 /* Fetch the child constraint tuple */
11894 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11896 partConstrFrelid = partConstr->confrelid;
11897 partConstrRelid = partConstr->conrelid;
11898
11899 /*
11900 * If the referenced table is partitioned, then the partition we're
11901 * attaching now has extra pg_constraint rows and action triggers that are
11902 * no longer needed. Remove those.
11903 */
11905 {
11907
11910
11912 }
11913
11914 /*
11915 * Will we need to validate this constraint? A valid parent constraint
11916 * implies that all child constraints have been validated, so if this one
11917 * isn't, we must trigger phase 3 validation.
11918 */
11919 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11920
11923
11924 /*
11925 * The action triggers in the new partition become redundant -- the parent
11926 * table already has equivalent ones, and those will be able to reach the
11927 * partition. Remove the ones in the partition. We identify them because
11928 * they have our constraint OID, as well as being on the referenced rel.
11929 */
11932
11935
11936 /*
11937 * Like the constraint, attach partition's "check" triggers to the
11938 * corresponding parent triggers if the constraint is ENFORCED. NOT
11939 * ENFORCED constraints do not have these triggers.
11940 */
11942 {
11945
11955 }
11956
11957 /*
11958 * We updated this pg_constraint row above to set its parent; validating
11959 * it will cause its convalidated flag to change, so we need CCI here. In
11960 * addition, we need it unconditionally for the rare case where the parent
11961 * table has *two* identical constraints; when reaching this function for
11962 * the second one, we must have made our changes visible, otherwise we
11963 * would try to attach both to this one.
11964 */
11966
11967 /* If validation is needed, put it in the queue now. */
11968 if (queueValidation)
11969 {
11971 Oid confrelid;
11972
11974
11977 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11978
11979 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11980
11981 /* Use the same lock as for AT_ValidateConstraint */
11986 }
11987}
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 20493 of file tablecmds.c.

20494{
20495 /*
20496 * Create an inheritance; the relevant checks are performed inside the
20497 * function.
20498 */
20499 CreateInheritance(attachrel, rel, true);
20500
20501 /* Update the pg_class entry. */
20502 StorePartitionBound(attachrel, rel, bound);
20503
20504 /* Ensure there exists a correct set of indexes in the partition. */
20506
20507 /* and triggers */
20509
20510 /*
20511 * Clone foreign key constraints. Callee is responsible for setting up
20512 * for phase 3 constraint verification.
20513 */
20515}
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition heap.c:4053
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)

References AttachPartitionEnsureIndexes(), CloneForeignKeyConstraints(), CloneRowTriggersToPartition(), CreateInheritance(), fb(), and StorePartitionBound().

Referenced by ATExecAttachPartition(), ATExecMergePartitions(), and ATExecSplitPartition().

◆ ATTypedTableRecursion()

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

Definition at line 6953 of file tablecmds.c.

6955{
6956 ListCell *child;
6957 List *children;
6958
6959 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6960
6961 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6963 cmd->behavior);
6964
6965 foreach(child, children)
6966 {
6967 Oid childrelid = lfirst_oid(child);
6969
6970 childrel = relation_open(childrelid, lockmode);
6972 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
6974 }
6975}
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition tablecmds.c:7156

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

1404{
1405 int natts;
1407 ListCell *l;
1408 TupleDesc desc;
1409 char *attname;
1410 Oid atttypid;
1411 int32 atttypmod;
1412 Oid attcollation;
1413 int attdim;
1414
1415 /*
1416 * allocate a new tuple descriptor
1417 */
1418 natts = list_length(columns);
1419 desc = CreateTemplateTupleDesc(natts);
1420
1421 attnum = 0;
1422
1423 foreach(l, columns)
1424 {
1425 ColumnDef *entry = lfirst(l);
1428
1429 /*
1430 * for each entry in the list, get the name and type information from
1431 * the list and have TupleDescInitEntry fill in the attribute
1432 * information we need.
1433 */
1434 attnum++;
1435
1436 attname = entry->colname;
1437 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1438
1440 if (aclresult != ACLCHECK_OK)
1442
1443 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1445 if (attdim > PG_INT16_MAX)
1446 ereport(ERROR,
1448 errmsg("too many array dimensions"));
1449
1450 if (entry->typeName->setof)
1451 ereport(ERROR,
1453 errmsg("column \"%s\" cannot be declared SETOF",
1454 attname)));
1455
1457 atttypid, atttypmod, attdim);
1458 att = TupleDescAttr(desc, attnum - 1);
1459
1460 /* Override TupleDescInitEntry's settings as requested */
1461 TupleDescInitEntryCollation(desc, attnum, attcollation);
1462
1463 /* Fill in additional stuff not handled by TupleDescInitEntry */
1464 att->attnotnull = entry->is_not_null;
1465 att->attislocal = entry->is_local;
1466 att->attinhcount = entry->inhcount;
1467 att->attidentity = entry->identity;
1468 att->attgenerated = entry->generated;
1469 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1470 if (entry->storage)
1471 att->attstorage = entry->storage;
1472 else if (entry->storage_name)
1473 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1474
1476 }
1477
1478 TupleDescFinalize(desc);
1479
1480 return desc;
1481}
NameData attname
bool is_not_null
Definition parsenodes.h:772
char identity
Definition parsenodes.h:778
char * storage_name
Definition parsenodes.h:775
char * colname
Definition parsenodes.h:767
TypeName * typeName
Definition parsenodes.h:768
char generated
Definition parsenodes.h:781
char storage
Definition parsenodes.h:774
bool is_local
Definition parsenodes.h:771
int16 inhcount
Definition parsenodes.h:770
char * compression
Definition parsenodes.h:769
bool setof
Definition parsenodes.h:287
TupleDesc CreateTemplateTupleDesc(int natts)
Definition tupdesc.c:165
void TupleDescFinalize(TupleDesc tupdesc)
Definition tupdesc.c:508
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition tupdesc.c:100
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
Definition tupdesc.c:1081
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition tupdesc.c:897

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

22372{
22373 /*
22374 * Build the needed expression execution states. Here, we expect only NOT
22375 * NULL and CHECK constraint.
22376 */
22378 {
22379 switch (con->contype)
22380 {
22381 case CONSTR_CHECK:
22382
22383 /*
22384 * We already expanded virtual expression in
22385 * createTableConstraints.
22386 */
22387 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22388 break;
22389 case CONSTR_NOTNULL:
22390 /* Nothing to do here. */
22391 break;
22392 default:
22393 elog(ERROR, "unrecognized constraint type: %d",
22394 (int) con->contype);
22395 }
22396 }
22397
22398 /* Expression already planned in createTableConstraints */
22400 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22401}
#define foreach_ptr(type, var, lst)
Definition pg_list.h:469

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

Referenced by MergePartitionsMoveRows(), and SplitPartitionMoveRows().

◆ change_owner_fix_column_acls()

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

Definition at line 16556 of file tablecmds.c.

16557{
16559 SysScanDesc scan;
16560 ScanKeyData key[1];
16562
16564 ScanKeyInit(&key[0],
16567 ObjectIdGetDatum(relationOid));
16569 true, NULL, 1, key);
16571 {
16576 Acl *newAcl;
16578 bool isNull;
16579 HeapTuple newtuple;
16580
16581 /* Ignore dropped columns */
16582 if (att->attisdropped)
16583 continue;
16584
16588 &isNull);
16589 /* Null ACLs do not require changes */
16590 if (isNull)
16591 continue;
16592
16593 memset(repl_null, false, sizeof(repl_null));
16594 memset(repl_repl, false, sizeof(repl_repl));
16595
16600
16604
16605 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16606
16607 heap_freetuple(newtuple);
16608 }
16609 systable_endscan(scan);
16611}

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

16622{
16624 SysScanDesc scan;
16625 ScanKeyData key[2];
16626 HeapTuple tup;
16627
16628 /*
16629 * SERIAL sequences are those having an auto dependency on one of the
16630 * table's columns (we don't care *which* column, exactly).
16631 */
16633
16634 ScanKeyInit(&key[0],
16638 ScanKeyInit(&key[1],
16641 ObjectIdGetDatum(relationOid));
16642 /* we leave refobjsubid unspecified */
16643
16645 NULL, 2, key);
16646
16647 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16648 {
16651
16652 /* skip dependencies other than auto dependencies on columns */
16653 if (depForm->refobjsubid == 0 ||
16654 depForm->classid != RelationRelationId ||
16655 depForm->objsubid != 0 ||
16656 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16657 continue;
16658
16659 /* Use relation_open just in case it's an index */
16660 seqRel = relation_open(depForm->objid, lockmode);
16661
16662 /* skip non-sequence relations */
16663 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16664 {
16665 /* No need to keep the lock */
16666 relation_close(seqRel, lockmode);
16667 continue;
16668 }
16669
16670 /* We don't need to close the sequence while we alter it. */
16671 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16672
16673 /* Now we can close it. Keep the lock till end of transaction. */
16675 }
16676
16677 systable_endscan(scan);
16678
16680}

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

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

References attnum, ereport, errcode(), errmsg, ERROR, fb(), GETSTRUCT(), HeapTupleIsValid, NOTICE, ObjectIdGetDatum(), PointerGetDatum(), RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), and SearchSysCache2().

Referenced by ATExecAddColumn(), and renameatt_internal().

◆ check_of_type()

void check_of_type ( HeapTuple  typetuple)

Definition at line 7205 of file tablecmds.c.

7206{
7208 bool typeOk = false;
7209
7210 if (typ->typtype == TYPTYPE_COMPOSITE)
7211 {
7213
7214 Assert(OidIsValid(typ->typrelid));
7216 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7217
7218 /*
7219 * Close the parent rel, but keep our AccessShareLock on it until xact
7220 * commit. That will prevent someone else from deleting or ALTERing
7221 * the type before the typed table creation/conversion commits.
7222 */
7224
7225 if (!typeOk)
7226 ereport(ERROR,
7228 errmsg("type %s is the row type of another table",
7229 format_type_be(typ->oid)),
7230 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7231 }
7232 else
7233 ereport(ERROR,
7235 errmsg("type %s is not a composite type",
7236 format_type_be(typ->oid))));
7237}

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

4481{
4482 /*
4483 * Don't allow ALTER on temp tables of other backends. Their local buffer
4484 * manager is not going to cope if we need to change the table's contents.
4485 * Even if we don't, there may be optimizations that assume temp tables
4486 * aren't subject to such interference.
4487 */
4488 if (RELATION_IS_OTHER_TEMP(rel))
4489 ereport(ERROR,
4491 errmsg("cannot alter temporary tables of other sessions")));
4492
4493 /*
4494 * Also check for active uses of the relation in the current transaction,
4495 * including open scans and pending AFTER trigger events.
4496 */
4497 CheckTableNotInUse(rel, "ALTER TABLE");
4498}
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition tablecmds.c:4447

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

13908{
13909 Oid roleid = GetUserId();
13911 int i;
13912
13913 /* Okay if we have relation-level REFERENCES permission */
13916 if (aclresult == ACLCHECK_OK)
13917 return;
13918 /* Else we must have REFERENCES on each column */
13919 for (i = 0; i < natts; i++)
13920 {
13922 roleid, ACL_REFERENCES);
13923 if (aclresult != ACLCHECK_OK)
13926 }
13927}
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 3724 of file tablecmds.c.

3725{
3727
3728 /*
3729 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3730 * stored as 0.
3731 */
3732 oldTableSpaceId = rel->rd_rel->reltablespace;
3735 return false;
3736
3737 /*
3738 * We cannot support moving mapped relations into different tablespaces.
3739 * (In particular this eliminates all shared catalogs.)
3740 */
3741 if (RelationIsMapped(rel))
3742 ereport(ERROR,
3744 errmsg("cannot move system relation \"%s\"",
3746
3747 /* Cannot move a non-shared relation into pg_global */
3749 ereport(ERROR,
3751 errmsg("only shared relations can be placed in pg_global tablespace")));
3752
3753 /*
3754 * Do not allow moving temp tables of other backends ... their local
3755 * buffer manager is not going to cope.
3756 */
3757 if (RELATION_IS_OTHER_TEMP(rel))
3758 ereport(ERROR,
3760 errmsg("cannot move temporary tables of other sessions")));
3761
3762 return true;
3763}
#define RelationIsMapped(relation)
Definition rel.h:563

References ereport, errcode(), errmsg, ERROR, fb(), MyDatabaseTableSpace, RelationData::rd_rel, RELATION_IS_OTHER_TEMP, RelationGetRelationName, and RelationIsMapped.

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

◆ CheckTableNotInUse()

void CheckTableNotInUse ( Relation  rel,
const char stmt 
)

Definition at line 4447 of file tablecmds.c.

4448{
4449 int expected_refcnt;
4450
4451 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4452 if (rel->rd_refcnt != expected_refcnt)
4453 ereport(ERROR,
4455 /* translator: first %s is a SQL command, eg ALTER TABLE */
4456 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4458
4459 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4460 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4462 ereport(ERROR,
4464 /* translator: first %s is a SQL command, eg ALTER TABLE */
4465 errmsg("cannot %s \"%s\" because it has pending trigger events",
4467}
int rd_refcnt
Definition rel.h:59
bool rd_isnailed
Definition rel.h:62
bool AfterTriggerPendingOnRel(Oid relid)
Definition trigger.c:6083

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

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

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

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

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

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

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

◆ ComputePartitionAttrs()

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

Definition at line 20024 of file tablecmds.c.

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

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

Referenced by DefineRelation().

◆ ConstraintImpliedByRelConstraint()

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

Definition at line 20353 of file tablecmds.c.

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

17711{
17714
17715 if (acon->condeferrable != bcon->condeferrable ||
17716 acon->condeferred != bcon->condeferred ||
17719 return false;
17720 else
17721 return true;
17722}
int b
Definition isn.c:74
int a
Definition isn.c:73
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)

References a, b, decompile_conbin(), fb(), Form_pg_constraint, and GETSTRUCT().

Referenced by MergeConstraintsIntoExisting().

◆ CreateFKCheckTrigger()

static Oid CreateFKCheckTrigger ( Oid  myRelOid,
Oid  refRelOid,
Constraint fkconstraint,
Oid  constraintOid,
Oid  indexOid,
Oid  parentTrigOid,
bool  on_insert 
)
static

Definition at line 14036 of file tablecmds.c.

14039{
14042
14043 /*
14044 * Note: for a self-referential FK (referencing and referenced tables are
14045 * the same), it is important that the ON UPDATE action fires before the
14046 * CHECK action, since both triggers will fire on the same row during an
14047 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
14048 * state of the row. Triggers fire in name order, so we ensure this by
14049 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
14050 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
14051 */
14053 fk_trigger->replace = false;
14054 fk_trigger->isconstraint = true;
14055 fk_trigger->trigname = "RI_ConstraintTrigger_c";
14056 fk_trigger->relation = NULL;
14057
14058 /* Either ON INSERT or ON UPDATE */
14059 if (on_insert)
14060 {
14061 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
14063 }
14064 else
14065 {
14066 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
14068 }
14069
14070 fk_trigger->args = NIL;
14071 fk_trigger->row = true;
14073 fk_trigger->columns = NIL;
14074 fk_trigger->whenClause = NULL;
14075 fk_trigger->transitionRels = NIL;
14076 fk_trigger->deferrable = fkconstraint->deferrable;
14077 fk_trigger->initdeferred = fkconstraint->initdeferred;
14078 fk_trigger->constrrel = NULL;
14079
14081 constraintOid, indexOid, InvalidOid,
14082 parentTrigOid, NULL, true, false);
14083
14084 /* Make changes-so-far visible */
14086
14087 return trigAddress.objectId;
14088}
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 14099 of file tablecmds.c.

14103{
14106
14107 /*
14108 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14109 * DELETE action on the referenced table.
14110 */
14112 fk_trigger->replace = false;
14113 fk_trigger->isconstraint = true;
14114 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14115 fk_trigger->relation = NULL;
14116 fk_trigger->args = NIL;
14117 fk_trigger->row = true;
14120 fk_trigger->columns = NIL;
14121 fk_trigger->whenClause = NULL;
14122 fk_trigger->transitionRels = NIL;
14123 fk_trigger->constrrel = NULL;
14124
14125 switch (fkconstraint->fk_del_action)
14126 {
14128 fk_trigger->deferrable = fkconstraint->deferrable;
14129 fk_trigger->initdeferred = fkconstraint->initdeferred;
14130 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
14131 break;
14133 fk_trigger->deferrable = false;
14134 fk_trigger->initdeferred = false;
14135 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
14136 break;
14138 fk_trigger->deferrable = false;
14139 fk_trigger->initdeferred = false;
14140 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
14141 break;
14143 fk_trigger->deferrable = false;
14144 fk_trigger->initdeferred = false;
14145 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
14146 break;
14148 fk_trigger->deferrable = false;
14149 fk_trigger->initdeferred = false;
14150 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
14151 break;
14152 default:
14153 elog(ERROR, "unrecognized FK action type: %d",
14154 (int) fkconstraint->fk_del_action);
14155 break;
14156 }
14157
14159 constraintOid, indexOid, InvalidOid,
14160 parentDelTrigger, NULL, true, false);
14161 if (deleteTrigOid)
14162 *deleteTrigOid = trigAddress.objectId;
14163
14164 /* Make changes-so-far visible */
14166
14167 /*
14168 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14169 * UPDATE action on the referenced table.
14170 */
14172 fk_trigger->replace = false;
14173 fk_trigger->isconstraint = true;
14174 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14175 fk_trigger->relation = NULL;
14176 fk_trigger->args = NIL;
14177 fk_trigger->row = true;
14180 fk_trigger->columns = NIL;
14181 fk_trigger->whenClause = NULL;
14182 fk_trigger->transitionRels = NIL;
14183 fk_trigger->constrrel = NULL;
14184
14185 switch (fkconstraint->fk_upd_action)
14186 {
14188 fk_trigger->deferrable = fkconstraint->deferrable;
14189 fk_trigger->initdeferred = fkconstraint->initdeferred;
14190 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
14191 break;
14193 fk_trigger->deferrable = false;
14194 fk_trigger->initdeferred = false;
14195 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
14196 break;
14198 fk_trigger->deferrable = false;
14199 fk_trigger->initdeferred = false;
14200 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
14201 break;
14203 fk_trigger->deferrable = false;
14204 fk_trigger->initdeferred = false;
14205 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
14206 break;
14208 fk_trigger->deferrable = false;
14209 fk_trigger->initdeferred = false;
14210 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
14211 break;
14212 default:
14213 elog(ERROR, "unrecognized FK action type: %d",
14214 (int) fkconstraint->fk_upd_action);
14215 break;
14216 }
14217
14219 constraintOid, indexOid, InvalidOid,
14220 parentUpdTrigger, NULL, true, false);
14221 if (updateTrigOid)
14222 *updateTrigOid = trigAddress.objectId;
14223}
#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 14234 of file tablecmds.c.

14239{
14241 constraintOid, indexOid,
14242 parentInsTrigger, true);
14244 constraintOid, indexOid,
14245 parentUpdTrigger, false);
14246}
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 17613 of file tablecmds.c.

17614{
17616 SysScanDesc scan;
17620
17621 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17623
17624 /*
17625 * Check for duplicates in the list of parents, and determine the highest
17626 * inhseqno already present; we'll use the next one for the new parent.
17627 * Also, if proposed child is a partition, it cannot already be
17628 * inheriting.
17629 *
17630 * Note: we do not reject the case where the child already inherits from
17631 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17632 */
17633 ScanKeyInit(&key,
17638 true, NULL, 1, &key);
17639
17640 /* inhseqno sequences start at 1 */
17641 inhseqno = 0;
17643 {
17645
17646 if (inh->inhparent == RelationGetRelid(parent_rel))
17647 ereport(ERROR,
17649 errmsg("relation \"%s\" would be inherited from more than once",
17651
17652 if (inh->inhseqno > inhseqno)
17653 inhseqno = inh->inhseqno;
17654 }
17655 systable_endscan(scan);
17656
17657 /* Match up the columns and bump attinhcount as needed */
17659
17660 /* Match up the constraints and bump coninhcount as needed */
17662
17663 /*
17664 * OK, it looks valid. Make the catalog entries that show inheritance.
17665 */
17668 inhseqno + 1,
17670 parent_rel->rd_rel->relkind ==
17672
17673 /* Now we're done with pg_inherits */
17675}
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:3596
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 22714 of file tablecmds.c.

22716{
22718 Oid newRelId;
22721 List *colList = NIL;
22722 Oid relamId;
22726
22727 /* If the existing rel is temp, it must belong to this session. */
22729 ereport(ERROR,
22731 errmsg("cannot create as partition of temporary relation of another session"));
22732
22733 /* Look up inheritance ancestors and generate the relation schema. */
22735
22736 /* Create a tuple descriptor from the relation schema. */
22738
22739 /* Look up the access method for the new relation. */
22741
22742 /* Look up the namespace in which we are supposed to create the relation. */
22743 namespaceId =
22746 ereport(ERROR,
22748 errmsg("relation \"%s\" already exists", newPartName->relname));
22749
22750 /*
22751 * We intended to create the partition with the same persistence as the
22752 * parent table, but we still need to recheck because that might be
22753 * affected by the search_path. If the parent is permanent, so must be
22754 * all of its partitions.
22755 */
22756 if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22757 newPartName->relpersistence == RELPERSISTENCE_TEMP)
22758 ereport(ERROR,
22760 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
22762
22763 /* Permanent rels cannot be partitions belonging to a temporary parent. */
22764 if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
22765 parent_relform->relpersistence == RELPERSISTENCE_TEMP)
22766 ereport(ERROR,
22768 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
22770
22771 /* Create the relation. */
22774 parent_relform->reltablespace,
22775 InvalidOid,
22776 InvalidOid,
22777 InvalidOid,
22778 ownerId,
22779 relamId,
22780 descriptor,
22781 NIL,
22783 newPartName->relpersistence,
22784 false,
22785 false,
22787 (Datum) 0,
22788 true,
22790 true,
22791 InvalidOid,
22792 NULL);
22793
22794 /*
22795 * We must bump the command counter to make the newly-created relation
22796 * tuple visible for opening.
22797 */
22799
22800 /*
22801 * Open the new partition with no lock, because we already have an
22802 * AccessExclusiveLock placed there after creation.
22803 */
22805
22806 /* Find or create a work queue entry for the newly created table. */
22808
22809 /* Create constraints, default values, and generated values. */
22811
22812 /*
22813 * Need to call CommandCounterIncrement, so a fresh relcache entry has
22814 * newly installed constraint info.
22815 */
22817
22818 return newRel;
22819}
Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid reltypeid, Oid reloftypeid, Oid ownerid, Oid accessmtd, TupleDesc tupdesc, List *cooked_constraints, char relkind, char relpersistence, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, bool allow_system_table_mods, bool is_internal, Oid relrewrite, ObjectAddress *typaddress)
Definition heap.c:1122
@ ONCOMMIT_NOOP
Definition primnodes.h:59
static void createTableConstraints(List **wqueue, AlteredTableInfo *tab, Relation parent_rel, Relation newRel)
static List * getAttributesList(Relation parent_rel)

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

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ createSplitPartitionContext()

static SplitPartitionContext * createSplitPartitionContext ( Relation  partRel)
static

Definition at line 23202 of file tablecmds.c.

23203{
23205
23207 pc->partRel = partRel;
23208
23209 /*
23210 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
23211 * don't bother using it.
23212 */
23213 pc->bistate = GetBulkInsertState();
23214
23215 /* Create a destination tuple slot for the new partition. */
23216 pc->dstslot = table_slot_create(pc->partRel, NULL);
23217
23218 return pc;
23219}
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 22507 of file tablecmds.c.

22509{
22511 TupleConstr *constr;
22512 AttrMap *attmap;
22514 int ccnum;
22515 List *constraints = NIL;
22517
22519 constr = tupleDesc->constr;
22520
22521 if (!constr)
22522 return;
22523
22524 /*
22525 * Construct a map from the parent relation's attnos to the child rel's.
22526 * This re-checks type match, etc, although it shouldn't be possible to
22527 * have a failure since both tables are locked.
22528 */
22530 tupleDesc,
22531 false);
22532
22533 /* Cycle for default values. */
22534 for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
22535 {
22537 parent_attno - 1);
22538
22539 /* Ignore dropped columns in the parent. */
22540 if (attribute->attisdropped)
22541 continue;
22542
22543 /* Copy the default, if present, and it should be copied. */
22544 if (attribute->atthasdef)
22545 {
22547 bool found_whole_row;
22548 AttrNumber num;
22549 Node *def;
22551
22552 if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
22554 else
22555 {
22556 this_default = TupleDescGetDefault(tupleDesc, attribute->attnum);
22557 if (this_default == NULL)
22558 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
22559 attribute->attnum, RelationGetRelationName(parent_rel));
22560 }
22561
22562 num = attmap->attnums[parent_attno - 1];
22563 def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
22564
22565 if (found_whole_row && attribute->attgenerated != '\0')
22566 elog(ERROR, "cannot convert whole-row table reference");
22567
22568 /* Add a pre-cooked default expression. */
22569 StoreAttrDefault(newRel, num, def, true);
22570
22571 /*
22572 * Stored generated column expressions in parent_rel might
22573 * reference the tableoid. newRel, parent_rel tableoid clear is
22574 * not the same. If so, these stored generated columns require
22575 * recomputation for newRel within MergePartitionsMoveRows.
22576 */
22577 if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
22578 {
22580 newval->attnum = num;
22581 newval->expr = expression_planner((Expr *) def);
22582 newval->is_generated = (attribute->attgenerated != '\0');
22583 tab->newvals = lappend(tab->newvals, newval);
22584 }
22585 }
22586 }
22587
22588 /* Cycle for CHECK constraints. */
22589 for (ccnum = 0; ccnum < constr->num_check; ccnum++)
22590 {
22591 char *ccname = constr->check[ccnum].ccname;
22592 char *ccbin = constr->check[ccnum].ccbin;
22593 bool ccenforced = constr->check[ccnum].ccenforced;
22594 bool ccnoinherit = constr->check[ccnum].ccnoinherit;
22595 bool ccvalid = constr->check[ccnum].ccvalid;
22597 bool found_whole_row;
22598 Constraint *constr;
22599
22600 /*
22601 * The partitioned table can not have a NO INHERIT check constraint
22602 * (see StoreRelCheck function for details).
22603 */
22604 Assert(!ccnoinherit);
22605
22607 1, 0,
22608 attmap,
22609 InvalidOid, &found_whole_row);
22610
22611 /*
22612 * For the moment we have to reject whole-row variables (as for CREATE
22613 * TABLE LIKE and inheritances).
22614 */
22615 if (found_whole_row)
22616 elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
22617 ccname,
22619
22620 constr = makeNode(Constraint);
22621 constr->contype = CONSTR_CHECK;
22622 constr->conname = pstrdup(ccname);
22623 constr->deferrable = false;
22624 constr->initdeferred = false;
22625 constr->is_enforced = ccenforced;
22626 constr->skip_validation = !ccvalid;
22627 constr->initially_valid = ccvalid;
22628 constr->is_no_inherit = ccnoinherit;
22629 constr->raw_expr = NULL;
22631 constr->location = -1;
22632 constraints = lappend(constraints, constr);
22633 }
22634
22635 /* Install all CHECK constraints. */
22637 false, true, true, NULL);
22638
22639 /* Make the additional catalog changes visible. */
22641
22642 /*
22643 * parent_rel check constraint expression may reference tableoid, so later
22644 * in MergePartitionsMoveRows, we need to evaluate the check constraint
22645 * again for the newRel. We can check whether the check constraint
22646 * contains a tableoid reference via pull_varattnos.
22647 */
22649 {
22650 if (!ccon->skip_validation)
22651 {
22652 Node *qual;
22653 Bitmapset *attnums = NULL;
22654
22655 Assert(ccon->contype == CONSTR_CHECK);
22657 pull_varattnos(qual, 1, &attnums);
22658
22659 /*
22660 * Add a check only if it contains a tableoid
22661 * (TableOidAttributeNumber).
22662 */
22664 attnums))
22665 {
22667
22669 newcon->name = ccon->name;
22670 newcon->contype = CONSTR_CHECK;
22671 newcon->qual = qual;
22672
22673 tab->constraints = lappend(tab->constraints, newcon);
22674 }
22675 }
22676 }
22677
22678 /* Don't need the cookedConstraints anymore. */
22680
22681 /* Reproduce not-null constraints. */
22682 if (constr->has_not_null)
22683 {
22684 List *nnconstraints;
22685
22686 /*
22687 * The "include_noinh" argument is false because a partitioned table
22688 * can't have NO INHERIT constraint.
22689 */
22691 false, false);
22692
22693 Assert(list_length(nnconstraints) > 0);
22694
22695 /*
22696 * We already set pg_attribute.attnotnull in createPartitionTable. No
22697 * need call set_attnotnull again.
22698 */
22699 AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, true, NULL);
22700 }
22701}
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
#define TableOidAttributeNumber
Definition sysattr.h:26
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition tupdesc.c:1149

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

17683{
17685 bool isnull;
17686 Datum attr;
17687 Datum expr;
17688
17690 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17691 if (isnull)
17692 elog(ERROR, "null conbin for constraint %u", con->oid);
17693
17694 expr = DirectFunctionCall2(pg_get_expr, attr,
17695 ObjectIdGetDatum(con->conrelid));
17696 return TextDatumGetCString(expr);
17697}
#define DirectFunctionCall2(func, arg1, arg2)
Definition fmgr.h:686
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition ruleutils.c:3031

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

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

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,
int  ti_options 
)
static

Definition at line 23225 of file tablecmds.c.

23226{
23227 ListCell *ltab;
23228
23230 FreeBulkInsertState(pc->bistate);
23231
23232 table_finish_bulk_insert(pc->partRel, ti_options);
23233
23234 /*
23235 * We don't need to process this pc->partRel so delete the ALTER TABLE
23236 * queue of it.
23237 */
23238 foreach(ltab, *wqueue)
23239 {
23241
23242 if (tab->relid == RelationGetRelid(pc->partRel))
23243 {
23245 break;
23246 }
23247 }
23248
23249 pfree(pc);
23250}
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 21386 of file tablecmds.c.

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

22982{
22983 /* Remove the pg_inherits row first. */
22985
22986 /*
22987 * Detaching the partition might involve TOAST table access, so ensure we
22988 * have a valid snapshot.
22989 */
22991
22992 /* Do the final part of detaching. */
22994
22996}

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

18400{
18402 SysScanDesc scan;
18403 ScanKeyData key[3];
18405
18407
18408 ScanKeyInit(&key[0],
18412 ScanKeyInit(&key[1],
18415 ObjectIdGetDatum(relid));
18416 ScanKeyInit(&key[2],
18419 Int32GetDatum(0));
18420
18422 NULL, 3, key);
18423
18425 {
18427
18428 if (dep->refclassid == refclassid &&
18429 dep->refobjid == refobjid &&
18430 dep->refobjsubid == 0 &&
18431 dep->deptype == deptype)
18433 }
18434
18435 systable_endscan(scan);
18437}

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

21755{
21757 SysScanDesc scan;
21760 ObjectAddresses *objects;
21761
21762 objects = new_object_addresses();
21763
21764 /*
21765 * Scan pg_trigger to search for all triggers on this rel.
21766 */
21771 true, NULL, 1, &skey);
21773 {
21776
21777 /* Ignore triggers that weren't cloned */
21778 if (!OidIsValid(pg_trigger->tgparentid))
21779 continue;
21780
21781 /*
21782 * Ignore internal triggers that are implementation objects of foreign
21783 * keys, because these will be detached when the foreign keys
21784 * themselves are.
21785 */
21786 if (OidIsValid(pg_trigger->tgconstrrelid))
21787 continue;
21788
21789 /*
21790 * This is ugly, but necessary: remove the dependency markings on the
21791 * trigger so that it can be removed.
21792 */
21799
21800 /* remember this trigger to remove it below */
21802 add_exact_object_address(&trig, objects);
21803 }
21804
21805 /* make the dependency removal visible to the deletion below */
21808
21809 /* done */
21810 free_object_addresses(objects);
21811 systable_endscan(scan);
21813}

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

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

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

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

◆ DropErrorMsgNonExistent()

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

Definition at line 1488 of file tablecmds.c.

1489{
1490 const struct dropmsgstrings *rentry;
1491
1492 if (rel->schemaname != NULL &&
1494 {
1495 if (!missing_ok)
1496 {
1497 ereport(ERROR,
1499 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1500 }
1501 else
1502 {
1504 (errmsg("schema \"%s\" does not exist, skipping",
1505 rel->schemaname)));
1506 }
1507 return;
1508 }
1509
1510 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1511 {
1512 if (rentry->kind == rightkind)
1513 {
1514 if (!missing_ok)
1515 {
1516 ereport(ERROR,
1517 (errcode(rentry->nonexistent_code),
1518 errmsg(rentry->nonexistent_msg, rel->relname)));
1519 }
1520 else
1521 {
1522 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1523 break;
1524 }
1525 }
1526 }
1527
1528 Assert(rentry->kind != '\0'); /* Should be impossible */
1529}
Oid LookupNamespaceNoError(const char *nspname)
Definition namespace.c:3427
char * schemaname
Definition primnodes.h:81
static const struct dropmsgstrings dropmsgstringarray[]
Definition tablecmds.c:257

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

1537{
1538 const struct dropmsgstrings *rentry;
1539 const struct dropmsgstrings *wentry;
1540
1541 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1542 if (rentry->kind == rightkind)
1543 break;
1544 Assert(rentry->kind != '\0');
1545
1546 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1547 if (wentry->kind == wrongkind)
1548 break;
1549 /* wrongkind could be something we don't have in our table... */
1550
1551 ereport(ERROR,
1553 errmsg(rentry->nota_msg, relname),
1554 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1555}

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

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

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

◆ evaluateGeneratedExpressionsAndCheckConstraints()

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

Definition at line 22409 of file tablecmds.c.

22413{
22414 econtext->ecxt_scantuple = insertslot;
22415
22417 {
22418 if (!ex->is_generated)
22419 continue;
22420
22421 insertslot->tts_values[ex->attnum - 1]
22422 = ExecEvalExpr(ex->exprstate,
22423 econtext,
22424 &insertslot->tts_isnull[ex->attnum - 1]);
22425 }
22426
22428 {
22429 switch (con->contype)
22430 {
22431 case CONSTR_CHECK:
22432 if (!ExecCheck(con->qualstate, econtext))
22433 ereport(ERROR,
22435 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
22437 errtableconstraint(newPartRel, con->name));
22438 break;
22439 case CONSTR_NOTNULL:
22440 case CONSTR_FOREIGN:
22441 /* Nothing to do here */
22442 break;
22443 default:
22444 elog(ERROR, "unrecognized constraint type: %d",
22445 (int) con->contype);
22446 }
22447 }
22448}
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 1890 of file tablecmds.c.

1891{
1892 List *rels = NIL;
1893 List *relids = NIL;
1895 ListCell *cell;
1896
1897 /*
1898 * Open, exclusive-lock, and check all the explicitly-specified relations
1899 */
1900 foreach(cell, stmt->relations)
1901 {
1902 RangeVar *rv = lfirst(cell);
1903 Relation rel;
1904 bool recurse = rv->inh;
1905 Oid myrelid;
1906 LOCKMODE lockmode = AccessExclusiveLock;
1907
1908 myrelid = RangeVarGetRelidExtended(rv, lockmode,
1910 NULL);
1911
1912 /* don't throw error for "TRUNCATE foo, foo" */
1913 if (list_member_oid(relids, myrelid))
1914 continue;
1915
1916 /* open the relation, we already hold a lock on it */
1917 rel = table_open(myrelid, NoLock);
1918
1919 /*
1920 * RangeVarGetRelidExtended() has done most checks with its callback,
1921 * but other checks with the now-opened Relation remain.
1922 */
1924
1925 rels = lappend(rels, rel);
1926 relids = lappend_oid(relids, myrelid);
1927
1928 /* Log this relation only if needed for logical decoding */
1931
1932 if (recurse)
1933 {
1934 ListCell *child;
1935 List *children;
1936
1937 children = find_all_inheritors(myrelid, lockmode, NULL);
1938
1939 foreach(child, children)
1940 {
1941 Oid childrelid = lfirst_oid(child);
1942
1943 if (list_member_oid(relids, childrelid))
1944 continue;
1945
1946 /* find_all_inheritors already got lock */
1948
1949 /*
1950 * It is possible that the parent table has children that are
1951 * temp tables of other backends. We cannot safely access
1952 * such tables (because of buffering issues), and the best
1953 * thing to do is to silently ignore them. Note that this
1954 * check is the same as one of the checks done in
1955 * truncate_check_activity() called below, still it is kept
1956 * here for simplicity.
1957 */
1958 if (RELATION_IS_OTHER_TEMP(rel))
1959 {
1960 table_close(rel, lockmode);
1961 continue;
1962 }
1963
1964 /*
1965 * Inherited TRUNCATE commands perform access permission
1966 * checks on the parent table only. So we skip checking the
1967 * children's permissions and don't call
1968 * truncate_check_perms() here.
1969 */
1972
1973 rels = lappend(rels, rel);
1974 relids = lappend_oid(relids, childrelid);
1975
1976 /* Log this relation only if needed for logical decoding */
1979 }
1980 }
1981 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1982 ereport(ERROR,
1984 errmsg("cannot truncate only a partitioned table"),
1985 errhint("Do not specify the ONLY keyword, or use TRUNCATE ONLY on the partitions directly.")));
1986 }
1987
1988 ExecuteTruncateGuts(rels, relids, relids_logged,
1989 stmt->behavior, stmt->restart_seqs, false);
1990
1991 /* And close the rels */
1992 foreach(cell, rels)
1993 {
1994 Relation rel = (Relation) lfirst(cell);
1995
1996 table_close(rel, NoLock);
1997 }
1998}
#define RelationIsLogicallyLogged(relation)
Definition rel.h:710
bool inh
Definition primnodes.h:87
static void truncate_check_activity(Relation rel)
Definition tablecmds.c:2470
static void truncate_check_rel(Oid relid, Form_pg_class reltuple)
Definition tablecmds.c:2401
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:2014

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

2019{
2020 List *rels;
2021 List *seq_relids = NIL;
2022 HTAB *ft_htab = NULL;
2023 EState *estate;
2025 ResultRelInfo *resultRelInfo;
2027 ListCell *cell;
2028 Oid *logrelids;
2029
2030 /*
2031 * Check the explicitly-specified relations.
2032 *
2033 * In CASCADE mode, suck in all referencing relations as well. This
2034 * requires multiple iterations to find indirectly-dependent relations. At
2035 * each phase, we need to exclusive-lock new rels before looking for their
2036 * dependencies, else we might miss something. Also, we check each rel as
2037 * soon as we open it, to avoid a faux pas such as holding lock for a long
2038 * time on a rel we have no permissions for.
2039 */
2040 rels = list_copy(explicit_rels);
2041 if (behavior == DROP_CASCADE)
2042 {
2043 for (;;)
2044 {
2045 List *newrelids;
2046
2048 if (newrelids == NIL)
2049 break; /* nothing else to add */
2050
2051 foreach(cell, newrelids)
2052 {
2053 Oid relid = lfirst_oid(cell);
2054 Relation rel;
2055
2056 rel = table_open(relid, AccessExclusiveLock);
2058 (errmsg("truncate cascades to table \"%s\"",
2060 truncate_check_rel(relid, rel->rd_rel);
2061 truncate_check_perms(relid, rel->rd_rel);
2063 rels = lappend(rels, rel);
2064 relids = lappend_oid(relids, relid);
2065
2066 /* Log this relation only if needed for logical decoding */
2069 }
2070 }
2071 }
2072
2073 /*
2074 * Check foreign key references. In CASCADE mode, this should be
2075 * unnecessary since we just pulled in all the references; but as a
2076 * cross-check, do it anyway if in an Assert-enabled build.
2077 */
2078#ifdef USE_ASSERT_CHECKING
2079 heap_truncate_check_FKs(rels, false);
2080#else
2081 if (behavior == DROP_RESTRICT)
2082 heap_truncate_check_FKs(rels, false);
2083#endif
2084
2085 /*
2086 * If we are asked to restart sequences, find all the sequences, lock them
2087 * (we need AccessExclusiveLock for ResetSequence), and check permissions.
2088 * We want to do this early since it's pointless to do all the truncation
2089 * work only to fail on sequence permissions.
2090 */
2091 if (restart_seqs)
2092 {
2093 foreach(cell, rels)
2094 {
2095 Relation rel = (Relation) lfirst(cell);
2098
2099 foreach(seqcell, seqlist)
2100 {
2103
2105
2106 /* This check must match AlterSequence! */
2110
2112
2114 }
2115 }
2116 }
2117
2118 /* Prepare to catch AFTER triggers. */
2120
2121 /*
2122 * To fire triggers, we'll need an EState as well as a ResultRelInfo for
2123 * each relation. We don't need to call ExecOpenIndices, though.
2124 *
2125 * We put the ResultRelInfos in the es_opened_result_relations list, even
2126 * though we don't have a range table and don't populate the
2127 * es_result_relations array. That's a bit bogus, but it's enough to make
2128 * ExecGetTriggerResultRel() find them.
2129 */
2130 estate = CreateExecutorState();
2132 palloc(list_length(rels) * sizeof(ResultRelInfo));
2133 resultRelInfo = resultRelInfos;
2134 foreach(cell, rels)
2135 {
2136 Relation rel = (Relation) lfirst(cell);
2137
2138 InitResultRelInfo(resultRelInfo,
2139 rel,
2140 0, /* dummy rangetable index */
2141 NULL,
2142 0);
2144 lappend(estate->es_opened_result_relations, resultRelInfo);
2145 resultRelInfo++;
2146 }
2147
2148 /*
2149 * Process all BEFORE STATEMENT TRUNCATE triggers before we begin
2150 * truncating (this is because one of them might throw an error). Also, if
2151 * we were to allow them to prevent statement execution, that would need
2152 * to be handled here.
2153 */
2154 resultRelInfo = resultRelInfos;
2155 foreach(cell, rels)
2156 {
2158
2160 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2161 &ucxt);
2162 ExecBSTruncateTriggers(estate, resultRelInfo);
2165 resultRelInfo++;
2166 }
2167
2168 /*
2169 * OK, truncate each table.
2170 */
2172
2173 foreach(cell, rels)
2174 {
2175 Relation rel = (Relation) lfirst(cell);
2176
2177 /* Skip partitioned tables as there is nothing to do */
2178 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2179 continue;
2180
2181 /*
2182 * Build the lists of foreign tables belonging to each foreign server
2183 * and pass each list to the foreign data wrapper's callback function,
2184 * so that each server can truncate its all foreign tables in bulk.
2185 * Each list is saved as a single entry in a hash table that uses the
2186 * server OID as lookup key.
2187 */
2188 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2189 {
2191 bool found;
2193
2194 /* First time through, initialize hashtable for foreign tables */
2195 if (!ft_htab)
2196 {
2197 HASHCTL hctl;
2198
2199 memset(&hctl, 0, sizeof(HASHCTL));
2200 hctl.keysize = sizeof(Oid);
2201 hctl.entrysize = sizeof(ForeignTruncateInfo);
2203
2204 ft_htab = hash_create("TRUNCATE for Foreign Tables",
2205 32, /* start small and extend */
2206 &hctl,
2208 }
2209
2210 /* Find or create cached entry for the foreign table */
2211 ft_info = hash_search(ft_htab, &serverid, HASH_ENTER, &found);
2212 if (!found)
2213 ft_info->rels = NIL;
2214
2215 /*
2216 * Save the foreign table in the entry of the server that the
2217 * foreign table belongs to.
2218 */
2219 ft_info->rels = lappend(ft_info->rels, rel);
2220 continue;
2221 }
2222
2223 /*
2224 * Normally, we need a transaction-safe truncation here. However, if
2225 * the table was either created in the current (sub)transaction or has
2226 * a new relfilenumber in the current (sub)transaction, then we can
2227 * just truncate it in-place, because a rollback would cause the whole
2228 * table or the current physical file to be thrown away anyway.
2229 */
2230 if (rel->rd_createSubid == mySubid ||
2232 {
2233 /* Immediate, non-rollbackable truncation is OK */
2235 }
2236 else
2237 {
2241
2242 /*
2243 * This effectively deletes all rows in the table, and may be done
2244 * in a serializable transaction. In that case we must record a
2245 * rw-conflict in to this transaction from each transaction
2246 * holding a predicate lock on the table.
2247 */
2249
2250 /*
2251 * Need the full transaction-safe pushups.
2252 *
2253 * Create a new empty storage file for the relation, and assign it
2254 * as the relfilenumber value. The old storage file is scheduled
2255 * for deletion at commit.
2256 */
2257 RelationSetNewRelfilenumber(rel, rel->rd_rel->relpersistence);
2258
2260
2261 /*
2262 * The same for the toast table, if any.
2263 */
2264 toast_relid = rel->rd_rel->reltoastrelid;
2266 {
2269
2271 toastrel->rd_rel->relpersistence);
2273 }
2274
2275 /*
2276 * Reconstruct the indexes to match, and we're done.
2277 */
2280 }
2281
2283 }
2284
2285 /* Now go through the hash table, and truncate foreign tables */
2286 if (ft_htab)
2287 {
2290
2292
2293 PG_TRY();
2294 {
2295 while ((ft_info = hash_seq_search(&seq)) != NULL)
2296 {
2297 FdwRoutine *routine = GetFdwRoutineByServerId(ft_info->serverid);
2298
2299 /* truncate_check_rel() has checked that already */
2300 Assert(routine->ExecForeignTruncate != NULL);
2301
2302 routine->ExecForeignTruncate(ft_info->rels,
2303 behavior,
2304 restart_seqs);
2305 }
2306 }
2307 PG_FINALLY();
2308 {
2310 }
2311 PG_END_TRY();
2312 }
2313
2314 /*
2315 * Restart owned sequences if we were asked to.
2316 */
2317 foreach(cell, seq_relids)
2318 {
2319 Oid seq_relid = lfirst_oid(cell);
2320
2322 }
2323
2324 /*
2325 * Write a WAL record to allow this set of actions to be logically
2326 * decoded.
2327 *
2328 * Assemble an array of relids so we can write a single WAL record for the
2329 * whole action.
2330 */
2331 if (relids_logged != NIL)
2332 {
2334 int i = 0;
2335
2336 /* should only get here if effective_wal_level is 'logical' */
2338
2340 foreach(cell, relids_logged)
2341 logrelids[i++] = lfirst_oid(cell);
2342
2343 xlrec.dbId = MyDatabaseId;
2344 xlrec.nrelids = list_length(relids_logged);
2345 xlrec.flags = 0;
2346 if (behavior == DROP_CASCADE)
2347 xlrec.flags |= XLH_TRUNCATE_CASCADE;
2348 if (restart_seqs)
2350
2354
2356
2358 }
2359
2360 /*
2361 * Process all AFTER STATEMENT TRUNCATE triggers.
2362 */
2363 resultRelInfo = resultRelInfos;
2364 foreach(cell, rels)
2365 {
2367
2369 SwitchToUntrustedUser(resultRelInfo->ri_RelationDesc->rd_rel->relowner,
2370 &ucxt);
2371 ExecASTruncateTriggers(estate, resultRelInfo);
2374 resultRelInfo++;
2375 }
2376
2377 /* Handle queued AFTER triggers */
2378 AfterTriggerEndQuery(estate);
2379
2380 /* We can clean up the EState now */
2381 FreeExecutorState(estate);
2382
2383 /*
2384 * Close any rels opened by CASCADE (can't do this while EState still
2385 * holds refs)
2386 */
2387 rels = list_difference_ptr(rels, explicit_rels);
2388 foreach(cell, rels)
2389 {
2390 Relation rel = (Relation) lfirst(cell);
2391
2392 table_close(rel, NoLock);
2393 }
2394}
uint32 SubTransactionId
Definition c.h:742
void ResetSequence(Oid seq_relid)
Definition sequence.c:255
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition dynahash.c:952
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition dynahash.c:358
void hash_destroy(HTAB *hashp)
Definition dynahash.c:865
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition dynahash.c:1415
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition dynahash.c:1380
#define PG_TRY(...)
Definition elog.h:372
#define PG_END_TRY(...)
Definition elog.h:397
#define PG_FINALLY(...)
Definition elog.h:389
FdwRoutine * GetFdwRoutineByServerId(Oid serverid)
Definition foreign.c:466
Oid GetForeignServerIdByRelId(Oid relid)
Definition foreign.c:444
List * heap_truncate_find_FKs(List *relationIds)
Definition heap.c:3770
void heap_truncate_check_FKs(List *relations, bool tempTables)
Definition heap.c:3675
void heap_truncate_one_rel(Relation rel)
Definition heap.c:3631
#define XLOG_HEAP_TRUNCATE
Definition heapam_xlog.h:36
#define XLH_TRUNCATE_RESTART_SEQS
#define SizeOfHeapTruncate
#define XLH_TRUNCATE_CASCADE
@ HASH_ENTER
Definition hsearch.h:114
#define HASH_CONTEXT
Definition hsearch.h:102
#define HASH_ELEM
Definition hsearch.h:95
#define HASH_BLOBS
Definition hsearch.h:97
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition index.c:3949
#define REINDEX_REL_PROCESS_TOAST
Definition index.h:165
List * list_difference_ptr(const List *list1, const List *list2)
Definition list.c:1263
void * palloc(Size size)
Definition mcxt.c:1387
@ DROP_CASCADE
@ OBJECT_SEQUENCE
void pgstat_count_truncate(Relation rel)
void CheckTableForSerializableConflictIn(Relation relation)
Definition predicate.c:4428
void RelationSetNewRelfilenumber(Relation relation, char persistence)
Definition relcache.c:3764
List * es_opened_result_relations
Definition execnodes.h:700
ExecForeignTruncate_function ExecForeignTruncate
Definition fdwapi.h:263
Size keysize
Definition hsearch.h:75
Size entrysize
Definition hsearch.h:76
MemoryContext hcxt
Definition hsearch.h:86
SubTransactionId rd_newRelfilelocatorSubid
Definition rel.h:104
Relation ri_RelationDesc
Definition execnodes.h:492
static void truncate_check_perms(Oid relid, Form_pg_class reltuple)
Definition tablecmds.c:2452
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:5137
void AfterTriggerBeginQuery(void)
Definition trigger.c:5117
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:136
#define XLOG_INCLUDE_ORIGIN
Definition xlog.h:165
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition xloginsert.c:479
void XLogRegisterData(const void *data, uint32 len)
Definition xloginsert.c:369
void XLogSetRecordFlags(uint8 flags)
Definition xloginsert.c:461
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 6998 of file tablecmds.c.

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

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

7157{
7159 ScanKeyData key[1];
7160 TableScanDesc scan;
7161 HeapTuple tuple;
7162 List *result = NIL;
7163
7165
7166 ScanKeyInit(&key[0],
7169 ObjectIdGetDatum(typeOid));
7170
7171 scan = table_beginscan_catalog(classRel, 1, key);
7172
7173 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7174 {
7176
7177 if (behavior == DROP_RESTRICT)
7178 ereport(ERROR,
7180 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7181 typeName),
7182 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7183 else
7184 result = lappend_oid(result, classform->oid);
7185 }
7186
7187 table_endscan(scan);
7189
7190 return result;
7191}

References AccessShareLock, BTEqualStrategyNumber, DROP_RESTRICT, ereport, errcode(), errhint(), errmsg, ERROR, fb(), ForwardScanDirection, GETSTRUCT(), heap_getnext(), lappend_oid(), NIL, ObjectIdGetDatum(), ScanKeyInit(), table_beginscan_catalog(), table_close(), table_endscan(), and table_open().

Referenced by ATTypedTableRecursion(), and renameatt_internal().

◆ findAttrByName()

static int findAttrByName ( const char attributeName,
const List columns 
)
static

Definition at line 3641 of file tablecmds.c.

3642{
3643 ListCell *lc;
3644 int i = 1;
3645
3646 foreach(lc, columns)
3647 {
3648 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3649 return i;
3650
3651 i++;
3652 }
3653 return 0;
3654}

References fb(), i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 13878 of file tablecmds.c.

13879{
13880 CoercionPathType ret;
13881
13883 {
13885 *funcid = InvalidOid;
13886 }
13887 else
13888 {
13890 COERCION_IMPLICIT, funcid);
13891 if (ret == COERCION_PATH_NONE)
13892 /* A previously-relied-upon cast is now gone. */
13893 elog(ERROR, "could not find cast from %u to %u",
13895 }
13896
13897 return ret;
13898}
CoercionPathType find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, Oid *funcid)
@ COERCION_PATH_NONE
@ COERCION_PATH_RELABELTYPE

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

Referenced by ATAddForeignKeyConstraint().

◆ GetAttributeCompression()

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

Definition at line 22293 of file tablecmds.c.

22294{
22295 char cmethod;
22296
22297 if (compression == NULL || strcmp(compression, "default") == 0)
22299
22300 /*
22301 * To specify a nondefault method, the column data type must be toastable.
22302 * Note this says nothing about whether the column's attstorage setting
22303 * permits compression; we intentionally allow attstorage and
22304 * attcompression to be independent. But with a non-toastable type,
22305 * attstorage could not be set to a value that would permit compression.
22306 *
22307 * We don't actually need to enforce this, since nothing bad would happen
22308 * if attcompression were non-default; it would never be consulted. But
22309 * it seems more user-friendly to complain about a certainly-useless
22310 * attempt to set the property.
22311 */
22313 ereport(ERROR,
22315 errmsg("column data type %s does not support compression",
22317
22318 cmethod = CompressionNameToMethod(compression);
22320 ereport(ERROR,
22322 errmsg("invalid compression method \"%s\"", compression)));
22323
22324 return cmethod;
22325}
#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 22454 of file tablecmds.c.

22455{
22458 List *colList = NIL;
22459
22461
22462 for (parent_attno = 1; parent_attno <= modelDesc->natts;
22463 parent_attno++)
22464 {
22466 parent_attno - 1);
22467 ColumnDef *def;
22468
22469 /* Ignore dropped columns in the parent. */
22470 if (attribute->attisdropped)
22471 continue;
22472
22473 def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
22474 attribute->atttypmod, attribute->attcollation);
22475
22476 def->is_not_null = attribute->attnotnull;
22477
22478 /* Copy identity. */
22479 def->identity = attribute->attidentity;
22480
22481 /* Copy attgenerated. */
22482 def->generated = attribute->attgenerated;
22483
22484 def->storage = attribute->attstorage;
22485
22486 /* Likewise, copy compression. */
22487 if (CompressionMethodIsValid(attribute->attcompression))
22488 def->compression =
22489 pstrdup(GetCompressionMethodName(attribute->attcompression));
22490 else
22491 def->compression = NULL;
22492
22493 /* Add to column list. */
22494 colList = lappend(colList, def);
22495 }
22496
22497 return colList;
22498}
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 22331 of file tablecmds.c.

22332{
22333 char cstorage = 0;
22334
22335 if (pg_strcasecmp(storagemode, "plain") == 0)
22337 else if (pg_strcasecmp(storagemode, "external") == 0)
22339 else if (pg_strcasecmp(storagemode, "extended") == 0)
22341 else if (pg_strcasecmp(storagemode, "main") == 0)
22343 else if (pg_strcasecmp(storagemode, "default") == 0)
22345 else
22346 ereport(ERROR,
22348 errmsg("invalid storage type \"%s\"",
22349 storagemode)));
22350
22351 /*
22352 * safety check: do not allow toasted storage modes unless column datatype
22353 * is TOAST-aware.
22354 */
22356 ereport(ERROR,
22358 errmsg("column data type %s can only have storage PLAIN",
22360
22361 return cstorage;
22362}
char get_typstorage(Oid typid)
Definition lsyscache.c:2641
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 12146 of file tablecmds.c.

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

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

22192{
22194 HeapTuple tuple;
22195 SysScanDesc scan;
22196 ScanKeyData key[2];
22197 List *constraints = NIL;
22198
22199 /*
22200 * If no indexes, or no columns are referenceable by FKs, we can avoid the
22201 * scan.
22202 */
22206 return NIL;
22207
22208 /* Search for constraints referencing this table */
22210 ScanKeyInit(&key[0],
22213 ScanKeyInit(&key[1],
22216
22217 /* XXX This is a seqscan, as we don't have a usable index */
22218 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
22219 while ((tuple = systable_getnext(scan)) != NULL)
22220 {
22222
22223 /*
22224 * We only need to process constraints that are part of larger ones.
22225 */
22226 if (!OidIsValid(constrForm->conparentid))
22227 continue;
22228
22229 constraints = lappend_oid(constraints, constrForm->oid);
22230 }
22231
22232 systable_endscan(scan);
22234
22235 return constraints;
22236}
#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 17390 of file tablecmds.c.

17391{
17393
17394 /*
17395 * Since we copy the file directly without looking at the shared buffers,
17396 * we'd better first flush out any pages of the source relation that are
17397 * in shared buffers. We assume no new changes will be made while we are
17398 * holding exclusive lock on the rel.
17399 */
17401
17402 /*
17403 * Create and copy all forks of the relation, and schedule unlinking of
17404 * old physical files.
17405 *
17406 * NOTE: any conflict in relfilenumber value will be caught in
17407 * RelationCreateStorage().
17408 */
17409 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17410
17411 /* copy main fork */
17413 rel->rd_rel->relpersistence);
17414
17415 /* copy those extra forks that exist */
17416 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17417 forkNum <= MAX_FORKNUM; forkNum++)
17418 {
17419 if (smgrexists(RelationGetSmgr(rel), forkNum))
17420 {
17421 smgrcreate(dstrel, forkNum, false);
17422
17423 /*
17424 * WAL log creation if the relation is persistent, or this is the
17425 * init fork of an unlogged relation.
17426 */
17427 if (RelationIsPermanent(rel) ||
17428 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17429 forkNum == INIT_FORKNUM))
17430 log_smgrcreate(&newrlocator, forkNum);
17432 rel->rd_rel->relpersistence);
17433 }
17434 }
17435
17436 /* drop old relation, and close new one */
17439}
void FlushRelationBuffers(Relation rel)
Definition bufmgr.c:5081
static SMgrRelation RelationGetSmgr(Relation rel)
Definition rel.h:576
ForkNumber
Definition relpath.h:56
@ MAIN_FORKNUM
Definition relpath.h:58
@ INIT_FORKNUM
Definition relpath.h:61
#define MAX_FORKNUM
Definition relpath.h:70
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition smgr.c:481
void smgrclose(SMgrRelation reln)
Definition smgr.c:374
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition smgr.c:462
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition storage.c:478
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition storage.c:122
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition storage.c:187
void RelationDropStorage(Relation rel)
Definition storage.c:207

References fb(), FlushRelationBuffers(), INIT_FORKNUM, log_smgrcreate(), MAIN_FORKNUM, MAX_FORKNUM, RelationData::rd_rel, RelationCopyStorage(), RelationCreateStorage(), RelationDropStorage(), RelationGetSmgr(), RelationIsPermanent, smgrclose(), smgrcreate(), and smgrexists().

Referenced by ATExecSetTableSpace().

◆ MarkInheritDetached()

static void MarkInheritDetached ( Relation  child_rel,
Relation  parent_rel 
)
static

Definition at line 18101 of file tablecmds.c.

18102{
18104 SysScanDesc scan;
18107 bool found = false;
18108
18109 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
18110
18111 /*
18112 * Find pg_inherits entries by inhparent. (We need to scan them all in
18113 * order to verify that no other partition is pending detach.)
18114 */
18116 ScanKeyInit(&key,
18121 true, NULL, 1, &key);
18122
18124 {
18126
18128 if (inhForm->inhdetachpending)
18129 ereport(ERROR,
18131 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
18132 get_rel_name(inhForm->inhrelid),
18133 get_namespace_name(parent_rel->rd_rel->relnamespace),
18135 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
18136
18137 if (inhForm->inhrelid == RelationGetRelid(child_rel))
18138 {
18140
18143
18145 &inheritsTuple->t_self,
18146 newtup);
18147 found = true;
18149 /* keep looking, to ensure we catch others pending detach */
18150 }
18151 }
18152
18153 /* Done */
18154 systable_endscan(scan);
18156
18157 if (!found)
18158 ereport(ERROR,
18160 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18163}
#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 2578 of file tablecmds.c.

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

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

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

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

3199{
3200 ListCell *lc;
3202
3203 foreach(lc, constraints)
3204 {
3206
3207 Assert(ccon->contype == CONSTR_CHECK);
3208
3209 /* Non-matching names never conflict */
3210 if (strcmp(ccon->name, name) != 0)
3211 continue;
3212
3213 if (equal(expr, ccon->expr))
3214 {
3215 /* OK to merge constraint with existing */
3216 if (pg_add_s16_overflow(ccon->inhcount, 1,
3217 &ccon->inhcount))
3218 ereport(ERROR,
3220 errmsg("too many inheritance parents"));
3221
3222 /*
3223 * When enforceability differs, the merged constraint should be
3224 * marked as ENFORCED because one of the parents is ENFORCED.
3225 */
3226 if (!ccon->is_enforced && is_enforced)
3227 {
3228 ccon->is_enforced = true;
3229 ccon->skip_validation = false;
3230 }
3231
3232 return constraints;
3233 }
3234
3235 ereport(ERROR,
3237 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3238 name)));
3239 }
3240
3241 /*
3242 * Constraint couldn't be merged with an existing one and also didn't
3243 * conflict with an existing one, so add it as a new one to the list.
3244 */
3246 newcon->contype = CONSTR_CHECK;
3247 newcon->name = pstrdup(name);
3248 newcon->expr = expr;
3249 newcon->inhcount = 1;
3250 newcon->is_enforced = is_enforced;
3251 newcon->skip_validation = !is_enforced;
3252 return lappend(constraints, newcon);
3253}

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

3278{
3279 char *attributeName = newdef->colname;
3281 Oid inhtypeid,
3282 newtypeid;
3284 newtypmod;
3285 Oid inhcollid,
3286 newcollid;
3287
3290 (errmsg("merging column \"%s\" with inherited definition",
3291 attributeName)));
3292 else
3294 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3295 errdetail("User-specified column moved to the position of the inherited column.")));
3296
3298
3299 /*
3300 * Must have the same type and typmod
3301 */
3305 ereport(ERROR,
3307 errmsg("column \"%s\" has a type conflict",
3309 errdetail("%s versus %s",
3312
3313 /*
3314 * Must have the same collation
3315 */
3318 if (inhcollid != newcollid)
3319 ereport(ERROR,
3321 errmsg("column \"%s\" has a collation conflict",
3323 errdetail("\"%s\" versus \"%s\"",
3326
3327 /*
3328 * Identity is never inherited by a regular inheritance child. Pick
3329 * child's identity definition if there's one.
3330 */
3331 inhdef->identity = newdef->identity;
3332
3333 /*
3334 * Copy storage parameter
3335 */
3336 if (inhdef->storage == 0)
3337 inhdef->storage = newdef->storage;
3338 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3339 ereport(ERROR,
3341 errmsg("column \"%s\" has a storage parameter conflict",
3343 errdetail("%s versus %s",
3344 storage_name(inhdef->storage),
3345 storage_name(newdef->storage))));
3346
3347 /*
3348 * Copy compression parameter
3349 */
3350 if (inhdef->compression == NULL)
3351 inhdef->compression = newdef->compression;
3352 else if (newdef->compression != NULL)
3353 {
3354 if (strcmp(inhdef->compression, newdef->compression) != 0)
3355 ereport(ERROR,
3357 errmsg("column \"%s\" has a compression method conflict",
3359 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3360 }
3361
3362 /*
3363 * Merge of not-null constraints = OR 'em together
3364 */
3365 inhdef->is_not_null |= newdef->is_not_null;
3366
3367 /*
3368 * Check for conflicts related to generated columns.
3369 *
3370 * If the parent column is generated, the child column will be made a
3371 * generated column if it isn't already. If it is a generated column,
3372 * we'll take its generation expression in preference to the parent's. We
3373 * must check that the child column doesn't specify a default value or
3374 * identity, which matches the rules for a single column in
3375 * parse_utilcmd.c.
3376 *
3377 * Conversely, if the parent column is not generated, the child column
3378 * can't be either. (We used to allow that, but it results in being able
3379 * to override the generation expression via UPDATEs through the parent.)
3380 */
3381 if (inhdef->generated)
3382 {
3383 if (newdef->raw_default && !newdef->generated)
3384 ereport(ERROR,
3386 errmsg("column \"%s\" inherits from generated column but specifies default",
3387 inhdef->colname)));
3388 if (newdef->identity)
3389 ereport(ERROR,
3391 errmsg("column \"%s\" inherits from generated column but specifies identity",
3392 inhdef->colname)));
3393 }
3394 else
3395 {
3396 if (newdef->generated)
3397 ereport(ERROR,
3399 errmsg("child column \"%s\" specifies generation expression",
3400 inhdef->colname),
3401 errhint("A child table column cannot be generated unless its parent column is.")));
3402 }
3403
3404 if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3405 ereport(ERROR,
3407 errmsg("column \"%s\" inherits from generated column of different kind",
3408 inhdef->colname),
3409 errdetail("Parent column is %s, child column is %s.",
3410 inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3411 newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3412
3413 /*
3414 * If new def has a default, override previous default
3415 */
3416 if (newdef->raw_default != NULL)
3417 {
3418 inhdef->raw_default = newdef->raw_default;
3419 inhdef->cooked_default = newdef->cooked_default;
3420 }
3421
3422 /* Mark the column as locally defined */
3423 inhdef->is_local = true;
3424}
static const char * storage_name(char c)
Definition tablecmds.c:2493

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

17878{
17883 Oid parent_relid = RelationGetRelid(parent_rel);
17884 AttrMap *attmap;
17885
17887
17888 /* Outer loop scans through the parent's constraint definitions */
17892 ObjectIdGetDatum(parent_relid));
17894 true, NULL, 1, &parent_key);
17895
17898 true);
17899
17901 {
17907 bool found = false;
17908
17909 if (parent_con->contype != CONSTRAINT_CHECK &&
17910 parent_con->contype != CONSTRAINT_NOTNULL)
17911 continue;
17912
17913 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17914 if (parent_con->connoinherit)
17915 continue;
17916
17917 if (parent_con->contype == CONSTRAINT_NOTNULL)
17919 else
17921
17922 /* Search for a child constraint matching this one */
17928 true, NULL, 1, &child_key);
17929
17931 {
17934
17935 if (child_con->contype != parent_con->contype)
17936 continue;
17937
17938 /*
17939 * CHECK constraint are matched by constraint name, NOT NULL ones
17940 * by attribute number.
17941 */
17942 if (child_con->contype == CONSTRAINT_CHECK)
17943 {
17944 if (strcmp(NameStr(parent_con->conname),
17945 NameStr(child_con->conname)) != 0)
17946 continue;
17947 }
17948 else if (child_con->contype == CONSTRAINT_NOTNULL)
17949 {
17953
17956 if (parent_attno != attmap->attnums[child_attno - 1])
17957 continue;
17958
17960 /* there shouldn't be constraints on dropped columns */
17961 if (parent_attr->attisdropped || child_attr->attisdropped)
17962 elog(ERROR, "found not-null constraint on dropped columns");
17963 }
17964
17965 if (child_con->contype == CONSTRAINT_CHECK &&
17967 ereport(ERROR,
17969 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17971
17972 /*
17973 * If the child constraint is "no inherit" then cannot merge
17974 */
17975 if (child_con->connoinherit)
17976 ereport(ERROR,
17978 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17980
17981 /*
17982 * If the child constraint is "not valid" then cannot merge with a
17983 * valid parent constraint
17984 */
17985 if (parent_con->convalidated && child_con->conenforced &&
17986 !child_con->convalidated)
17987 ereport(ERROR,
17989 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17991
17992 /*
17993 * A NOT ENFORCED child constraint cannot be merged with an
17994 * ENFORCED parent constraint. However, the reverse is allowed,
17995 * where the child constraint is ENFORCED.
17996 */
17997 if (parent_con->conenforced && !child_con->conenforced)
17998 ereport(ERROR,
18000 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
18002
18003 /*
18004 * OK, bump the child constraint's inheritance count. (If we fail
18005 * later on, this change will just roll back.)
18006 */
18009
18010 if (pg_add_s16_overflow(child_con->coninhcount, 1,
18011 &child_con->coninhcount))
18012 ereport(ERROR,
18014 errmsg("too many inheritance parents"));
18015
18016 /*
18017 * In case of partitions, an inherited constraint must be
18018 * inherited only once since it cannot have multiple parents and
18019 * it is never considered local.
18020 */
18021 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18022 {
18023 Assert(child_con->coninhcount == 1);
18024 child_con->conislocal = false;
18025 }
18026
18029
18030 found = true;
18031 break;
18032 }
18033
18035
18036 if (!found)
18037 {
18038 if (parent_con->contype == CONSTRAINT_NOTNULL)
18039 ereport(ERROR,
18041 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
18042 get_attname(parent_relid,
18044 false),
18046
18047 ereport(ERROR,
18049 errmsg("child table is missing constraint \"%s\"",
18050 NameStr(parent_con->conname))));
18051 }
18052 }
18053
18056}
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 3447 of file tablecmds.c.

3450{
3451 char *attributeName = newdef->colname;
3454 newtypeid;
3456 newtypmod;
3458 newcollid;
3459
3461 (errmsg("merging multiple inherited definitions of column \"%s\"",
3462 attributeName)));
3464
3465 /*
3466 * Must have the same type and typmod
3467 */
3471 ereport(ERROR,
3473 errmsg("inherited column \"%s\" has a type conflict",
3475 errdetail("%s versus %s",
3478
3479 /*
3480 * Must have the same collation
3481 */
3484 if (prevcollid != newcollid)
3485 ereport(ERROR,
3487 errmsg("inherited column \"%s\" has a collation conflict",
3489 errdetail("\"%s\" versus \"%s\"",
3492
3493 /*
3494 * Copy/check storage parameter
3495 */
3496 if (prevdef->storage == 0)
3497 prevdef->storage = newdef->storage;
3498 else if (prevdef->storage != newdef->storage)
3499 ereport(ERROR,
3501 errmsg("inherited column \"%s\" has a storage parameter conflict",
3503 errdetail("%s versus %s",
3504 storage_name(prevdef->storage),
3505 storage_name(newdef->storage))));
3506
3507 /*
3508 * Copy/check compression parameter
3509 */
3510 if (prevdef->compression == NULL)
3511 prevdef->compression = newdef->compression;
3512 else if (newdef->compression != NULL)
3513 {
3514 if (strcmp(prevdef->compression, newdef->compression) != 0)
3515 ereport(ERROR,
3517 errmsg("column \"%s\" has a compression method conflict",
3519 errdetail("%s versus %s",
3520 prevdef->compression, newdef->compression)));
3521 }
3522
3523 /*
3524 * Check for GENERATED conflicts
3525 */
3526 if (prevdef->generated != newdef->generated)
3527 ereport(ERROR,
3529 errmsg("inherited column \"%s\" has a generation conflict",
3530 attributeName)));
3531
3532 /*
3533 * Default and other constraints are handled by the caller.
3534 */
3535
3536 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3537 &prevdef->inhcount))
3538 ereport(ERROR,
3540 errmsg("too many inheritance parents"));
3541
3542 return prevdef;
3543}

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

22828{
22829 CommandId mycid;
22830 EState *estate;
22831 AlteredTableInfo *tab;
22832 ListCell *ltab;
22833
22834 /* The FSM is empty, so don't bother using it. */
22835 int ti_options = TABLE_INSERT_SKIP_FSM;
22836 BulkInsertState bistate; /* state of bulk inserts for partition */
22837 TupleTableSlot *dstslot;
22838
22839 /* Find the work queue entry for the new partition table: newPartRel. */
22841
22842 /* Generate the constraint and default execution states. */
22843 estate = CreateExecutorState();
22844
22846
22847 mycid = GetCurrentCommandId(true);
22848
22849 /* Prepare a BulkInsertState for table_tuple_insert. */
22850 bistate = GetBulkInsertState();
22851
22852 /* Create the necessary tuple slot. */
22853 dstslot = table_slot_create(newPartRel, NULL);
22854
22856 {
22857 ExprContext *econtext;
22860 TableScanDesc scan;
22862 Snapshot snapshot;
22864
22865 econtext = GetPerTupleExprContext(estate);
22866
22867 /*
22868 * Partition is already locked in the transformPartitionCmdForMerge
22869 * function.
22870 */
22872
22873 /* Create a source tuple slot for the partition being merged. */
22875
22876 /*
22877 * Map computing for moving attributes of the merged partition to the
22878 * new partition.
22879 */
22882
22883 /* Scan through the rows. */
22884 snapshot = RegisterSnapshot(GetLatestSnapshot());
22885 scan = table_beginscan(mergingPartition, snapshot, 0, NULL);
22886
22887 /*
22888 * Switch to per-tuple memory context and reset it for each tuple
22889 * produced, so we don't leak memory.
22890 */
22892
22894 {
22896
22898
22899 if (tuple_map)
22900 {
22901 /* Need to use a map to copy attributes. */
22902 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
22903 }
22904 else
22905 {
22907
22908 /* Copy attributes directly. */
22909 insertslot = dstslot;
22910
22912
22913 memcpy(insertslot->tts_values, srcslot->tts_values,
22914 sizeof(Datum) * srcslot->tts_nvalid);
22915 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
22916 sizeof(bool) * srcslot->tts_nvalid);
22917
22919 }
22920
22921 /*
22922 * Constraints and GENERATED expressions might reference the
22923 * tableoid column, so fill tts_tableOid with the desired value.
22924 * (We must do this each time, because it gets overwritten with
22925 * newrel's OID during storing.)
22926 */
22927 insertslot->tts_tableOid = RelationGetRelid(newPartRel);
22928
22929 /*
22930 * Now, evaluate any generated expressions whose inputs come from
22931 * the new tuple. We assume these columns won't reference each
22932 * other, so that there's no ordering dependency.
22933 */
22935 insertslot, econtext);
22936
22937 /* Write the tuple out to the new relation. */
22939 ti_options, bistate);
22940
22941 ResetExprContext(econtext);
22942 }
22943
22945 table_endscan(scan);
22946 UnregisterSnapshot(snapshot);
22947
22948 if (tuple_map)
22950
22953 }
22954
22955 FreeExecutorState(estate);
22957 FreeBulkInsertState(bistate);
22958
22960
22961 /*
22962 * We don't need to process this newPartRel since we already processed it
22963 * here, so delete the ALTER TABLE queue for it.
22964 */
22965 foreach(ltab, *wqueue)
22966 {
22967 tab = (AlteredTableInfo *) lfirst(ltab);
22968 if (tab->relid == RelationGetRelid(newPartRel))
22969 {
22971 break;
22972 }
22973 }
22974}
static void evaluateGeneratedExpressionsAndCheckConstraints(AlteredTableInfo *tab, Relation newPartRel, TupleTableSlot *insertslot, ExprContext *econtext)
static void buildExpressionExecutionStates(AlteredTableInfo *tab, Relation newPartRel, EState *estate)
TupleConversionMap * convert_tuples_by_name(TupleDesc indesc, TupleDesc outdesc)
Definition tupconvert.c:103
void free_conversion_map(TupleConversionMap *map)
Definition tupconvert.c:300
TupleTableSlot * execute_attr_map_slot(AttrMap *attrMap, TupleTableSlot *in_slot, TupleTableSlot *out_slot)
Definition tupconvert.c:193

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

Referenced by ATExecMergePartitions().

◆ NotNullImpliedByRelConstraints()

static bool NotNullImpliedByRelConstraints ( Relation  rel,
Form_pg_attribute  attr 
)
static

Definition at line 8176 of file tablecmds.c.

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

20300{
20302 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20303 int i;
20304
20305 if (constr && constr->has_not_null)
20306 {
20307 int natts = scanrel->rd_att->natts;
20308
20309 for (i = 1; i <= natts; i++)
20310 {
20312
20313 /* invalid not-null constraint must be ignored here */
20314 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20315 {
20318
20319 ntest->arg = (Expr *) makeVar(1,
20320 i,
20321 wholeatt->atttypid,
20322 wholeatt->atttypmod,
20323 wholeatt->attcollation,
20324 0);
20325 ntest->nulltesttype = IS_NOT_NULL;
20326
20327 /*
20328 * argisrow=false is correct even for a composite column,
20329 * because attnotnull does not represent a SQL-spec IS NOT
20330 * NULL test in such a case, just IS DISTINCT FROM NULL.
20331 */
20332 ntest->argisrow = false;
20333 ntest->location = -1;
20335 }
20336 }
20337 }
20338
20340}
bool has_not_null
Definition tupdesc.h:45

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

Referenced by check_default_partition_contents(), and QueuePartitionConstraintValidation().

◆ PreCommit_on_commit_actions()

void PreCommit_on_commit_actions ( void  )

Definition at line 19554 of file tablecmds.c.

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

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

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

13242{
13244 AlteredTableInfo *tab;
13247
13249 Assert(con->contype == CONSTRAINT_FOREIGN);
13250 Assert(!con->convalidated);
13251
13252 /*
13253 * Add the validation to phase 3's queue; not needed for partitioned
13254 * tables themselves, only for their partitions.
13255 *
13256 * When the referenced table (pkrelid) is partitioned, the referencing
13257 * table (fkrel) has one pg_constraint row pointing to each partition
13258 * thereof. These rows are there only to support action triggers and no
13259 * table scan is needed, therefore skip this for them as well.
13260 */
13261 if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13262 con->confrelid == pkrelid)
13263 {
13266
13267 /* Queue validation for phase 3 */
13269 /* for now this is all we need */
13270 fkconstraint->conname = pstrdup(NameStr(con->conname));
13271
13273 newcon->name = fkconstraint->conname;
13274 newcon->contype = CONSTR_FOREIGN;
13275 newcon->refrelid = con->confrelid;
13276 newcon->refindid = con->conindid;
13277 newcon->conid = con->oid;
13278 newcon->qual = (Node *) fkconstraint;
13279
13280 /* Find or create work queue entry for this table */
13282 tab->constraints = lappend(tab->constraints, newcon);
13283 }
13284
13285 /*
13286 * If the table at either end of the constraint is partitioned, we need to
13287 * recurse and handle every unvalidated constraint that is a child of this
13288 * constraint.
13289 */
13290 if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13291 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13292 {
13293 ScanKeyData pkey;
13296
13297 ScanKeyInit(&pkey,
13300 ObjectIdGetDatum(con->oid));
13301
13303 true, NULL, 1, &pkey);
13304
13306 {
13309
13311
13312 /*
13313 * If the child constraint has already been validated, no further
13314 * action is required for it or its descendants, as they are all
13315 * valid.
13316 */
13317 if (childcon->convalidated)
13318 continue;
13319
13320 childrel = table_open(childcon->conrelid, lockmode);
13321
13322 /*
13323 * NB: Note that pkrelid should be passed as-is during recursion,
13324 * as it is required to identify the root referenced table.
13325 */
13327 childtup, lockmode);
13329 }
13330
13332 }
13333
13334 /*
13335 * Now mark the pg_constraint row as validated (even if we didn't check,
13336 * notably the ones for partitions on the referenced side).
13337 *
13338 * We rely on transaction abort to roll back this change if phase 3
13339 * ultimately finds violating rows. This is a bit ugly.
13340 */
13343 copy_con->convalidated = true;
13345
13347
13349}

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

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

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

20419{
20420 /*
20421 * Based on the table's existing constraints, determine whether or not we
20422 * may skip scanning the table.
20423 */
20425 {
20426 if (!validate_default)
20428 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20429 RelationGetRelationName(scanrel))));
20430 else
20432 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20433 RelationGetRelationName(scanrel))));
20434 return;
20435 }
20436
20437 /*
20438 * Constraints proved insufficient. For plain relations, queue a
20439 * validation item now; for partitioned tables, recurse to process each
20440 * partition.
20441 */
20442 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20443 {
20444 AlteredTableInfo *tab;
20445
20446 /* Grab a work queue entry. */
20447 tab = ATGetQueueEntry(wqueue, scanrel);
20450 tab->validate_default = validate_default;
20451 }
20452 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20453 {
20454 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20455 int i;
20456
20457 for (i = 0; i < partdesc->nparts; i++)
20458 {
20461
20462 /*
20463 * This is the minimum lock we need to prevent deadlocks.
20464 */
20466
20467 /*
20468 * Adjust the constraint for scanrel so that it matches this
20469 * partition's attribute numbers.
20470 */
20473 part_rel, scanrel);
20474
20477 validate_default);
20478 table_close(part_rel, NoLock); /* keep lock till commit */
20479 }
20480 }
20481}
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 19820 of file tablecmds.c.

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

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

1733{
1734 HeapTuple tuple;
1736 char expected_relkind;
1737 bool is_partition;
1740 bool invalid_system_index = false;
1741
1742 state = (struct DropRelationCallbackState *) arg;
1743 heap_lockmode = state->heap_lockmode;
1744
1745 /*
1746 * If we previously locked some other index's heap, and the name we're
1747 * looking up no longer refers to that relation, release the now-useless
1748 * lock.
1749 */
1750 if (relOid != oldRelOid && OidIsValid(state->heapOid))
1751 {
1753 state->heapOid = InvalidOid;
1754 }
1755
1756 /*
1757 * Similarly, if we previously locked some other partition's heap, and the
1758 * name we're looking up no longer refers to that relation, release the
1759 * now-useless lock.
1760 */
1761 if (relOid != oldRelOid && OidIsValid(state->partParentOid))
1762 {
1764 state->partParentOid = InvalidOid;
1765 }
1766
1767 /* Didn't find a relation, so no need for locking or permission checks. */
1768 if (!OidIsValid(relOid))
1769 return;
1770
1771 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
1772 if (!HeapTupleIsValid(tuple))
1773 return; /* concurrently dropped, so nothing to do */
1775 is_partition = classform->relispartition;
1776
1777 /* Pass back some data to save lookups in RemoveRelations */
1778 state->actual_relkind = classform->relkind;
1779 state->actual_relpersistence = classform->relpersistence;
1780
1781 /*
1782 * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1783 * but RemoveRelations() can only pass one relkind for a given relation.
1784 * It chooses RELKIND_RELATION for both regular and partitioned tables.
1785 * That means we must be careful before giving the wrong type error when
1786 * the relation is RELKIND_PARTITIONED_TABLE. An equivalent problem
1787 * exists with indexes.
1788 */
1789 if (classform->relkind == RELKIND_PARTITIONED_TABLE)
1791 else if (classform->relkind == RELKIND_PARTITIONED_INDEX)
1793 else
1794 expected_relkind = classform->relkind;
1795
1796 if (state->expected_relkind != expected_relkind)
1798 state->expected_relkind);
1799
1800 /* Allow DROP to either table owner or schema owner */
1805 rel->relname);
1806
1807 /*
1808 * Check the case of a system index that might have been invalidated by a
1809 * failed concurrent process and allow its drop. For the time being, this
1810 * only concerns indexes of toast relations that became invalid during a
1811 * REINDEX CONCURRENTLY process.
1812 */
1813 if (IsSystemClass(relOid, classform) && classform->relkind == RELKIND_INDEX)
1814 {
1817 bool indisvalid;
1818
1821 {
1822 ReleaseSysCache(tuple);
1823 return;
1824 }
1825
1827 indisvalid = indexform->indisvalid;
1829
1830 /* Mark object as being an invalid index of system catalogs */
1831 if (!indisvalid)
1832 invalid_system_index = true;
1833 }
1834
1835 /* In the case of an invalid index, it is fine to bypass this check */
1837 ereport(ERROR,
1839 errmsg("permission denied: \"%s\" is a system catalog",
1840 rel->relname)));
1841
1842 ReleaseSysCache(tuple);
1843
1844 /*
1845 * In DROP INDEX, attempt to acquire lock on the parent table before
1846 * locking the index. index_drop() will need this anyway, and since
1847 * regular queries lock tables before their indexes, we risk deadlock if
1848 * we do it the other way around. No error if we don't find a pg_index
1849 * entry, though --- the relation may have been dropped. Note that this
1850 * code will execute for either plain or partitioned indexes.
1851 */
1853 relOid != oldRelOid)
1854 {
1855 state->heapOid = IndexGetRelation(relOid, true);
1856 if (OidIsValid(state->heapOid))
1858 }
1859
1860 /*
1861 * Similarly, if the relation is a partition, we must acquire lock on its
1862 * parent before locking the partition. That's because queries lock the
1863 * parent before its partitions, so we risk deadlock if we do it the other
1864 * way around.
1865 */
1866 if (is_partition && relOid != oldRelOid)
1867 {
1868 state->partParentOid = get_partition_parent(relOid, true);
1869 if (OidIsValid(state->partParentOid))
1870 LockRelationOid(state->partParentOid, AccessExclusiveLock);
1871 }
1872}
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:1536

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

4022{
4023 HeapTuple tuple;
4025
4026 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
4027 if (!HeapTupleIsValid(tuple))
4028 return; /* concurrently dropped */
4029 form = (Form_pg_class) GETSTRUCT(tuple);
4030 renameatt_check(relid, form, false);
4031 ReleaseSysCache(tuple);
4032}
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition tablecmds.c:3826

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

19766{
19767 HeapTuple tuple;
19768
19769 /* Nothing to do if the relation was not found. */
19770 if (!OidIsValid(relId))
19771 return;
19772
19773 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19774 if (!HeapTupleIsValid(tuple)) /* should not happen */
19775 elog(ERROR, "cache lookup failed for relation %u", relId);
19776
19779
19780 ReleaseSysCache(tuple);
19781}

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

19730{
19731 char relkind;
19733
19734 /* Nothing to do if the relation was not found. */
19735 if (!OidIsValid(relId))
19736 return;
19737
19738 /*
19739 * If the relation does exist, check whether it's an index. But note that
19740 * the relation might have been dropped between the time we did the name
19741 * lookup and now. In that case, there's nothing to do.
19742 */
19743 relkind = get_rel_relkind(relId);
19744 if (!relkind)
19745 return;
19746 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19747 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19748 ereport(ERROR,
19750 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19751
19752 /* Check permissions */
19754 if (aclresult != ACLCHECK_OK)
19757 relation->relname);
19758}
#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 19788 of file tablecmds.c.

19790{
19791 HeapTuple tuple;
19792
19793 /* Nothing to do if the relation was not found. */
19794 if (!OidIsValid(relId))
19795 return;
19796
19797 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
19798 if (!HeapTupleIsValid(tuple)) /* should not happen */
19799 elog(ERROR, "cache lookup failed for relation %u", relId);
19800
19803 relation->relname);
19804
19805 if (!allowSystemTableMods &&
19806 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
19807 ereport(ERROR,
19809 errmsg("permission denied: \"%s\" is a system catalog",
19810 relation->relname)));
19811
19812 ReleaseSysCache(tuple);
19813}

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

16087{
16088 CommentStmt *cmd;
16089 char *comment_str;
16091
16092 /* Look for comment for object wanted, and leave if none */
16094 if (comment_str == NULL)
16095 return;
16096
16097 /* Build CommentStmt node, copying all input data for safety */
16098 cmd = makeNode(CommentStmt);
16099 if (rel)
16100 {
16102 cmd->object = (Node *)
16105 makeString(pstrdup(conname)));
16106 }
16107 else
16108 {
16110 cmd->object = (Node *)
16112 makeString(pstrdup(conname)));
16113 }
16114 cmd->comment = comment_str;
16115
16116 /* Append it to list of commands */
16118 newcmd->subtype = AT_ReAddComment;
16119 newcmd->def = (Node *) cmd;
16120 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
16121}
TypeName * makeTypeNameFromNameList(List *names)
Definition makefuncs.c:531
@ OBJECT_TABCONSTRAINT
@ OBJECT_DOMCONSTRAINT
#define list_make3(x1, x2, x3)
Definition pg_list.h:216
#define list_make2(x1, x2)
Definition pg_list.h:214
char * comment
ObjectType objtype
Node * object

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

Referenced by ATPostAlterTypeParse().

◆ refuseDupeIndexAttach()

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

Definition at line 22043 of file tablecmds.c.

22044{
22046
22047 existingIdx = index_get_partition(partitionTbl,
22048 RelationGetRelid(parentIdx));
22050 ereport(ERROR,
22052 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22054 RelationGetRelationName(parentIdx)),
22055 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
22057 RelationGetRelationName(partitionTbl))));
22058}

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

19496{
19499
19500 /*
19501 * We needn't bother registering the relation unless there is an ON COMMIT
19502 * action we need to take.
19503 */
19504 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19505 return;
19506
19508
19510 oc->relid = relid;
19511 oc->oncommit = action;
19512 oc->creating_subid = GetCurrentSubTransactionId();
19513 oc->deleting_subid = InvalidSubTransactionId;
19514
19515 /*
19516 * We use lcons() here so that ON COMMIT actions are processed in reverse
19517 * order of registration. That might not be essential but it seems
19518 * reasonable.
19519 */
19521
19523}
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 18636 of file tablecmds.c.

18638{
18645 ListCell *index;
18646
18647 /*
18648 * Check whether relreplident has changed, and update it if so.
18649 */
18654 elog(ERROR, "cache lookup failed for relation \"%s\"",
18657 if (pg_class_form->relreplident != ri_type)
18658 {
18659 pg_class_form->relreplident = ri_type;
18661 }
18664
18665 /*
18666 * Update the per-index indisreplident flags correctly.
18667 */
18669 foreach(index, RelationGetIndexList(rel))
18670 {
18672 bool dirty = false;
18673
18677 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18679
18680 if (thisIndexOid == indexOid)
18681 {
18682 /* Set the bit if not already set. */
18683 if (!pg_index_form->indisreplident)
18684 {
18685 dirty = true;
18686 pg_index_form->indisreplident = true;
18687 }
18688 }
18689 else
18690 {
18691 /* Unset the bit if set. */
18692 if (pg_index_form->indisreplident)
18693 {
18694 dirty = true;
18695 pg_index_form->indisreplident = false;
18696 }
18697 }
18698
18699 if (dirty)
18700 {
18703 InvalidOid, is_internal);
18704
18705 /*
18706 * Invalidate the relcache for the table, so that after we commit
18707 * all sessions will refresh the table's replica identity index
18708 * before attempting any UPDATE or DELETE on the table. (If we
18709 * changed the table's pg_class row above, then a relcache inval
18710 * is already queued due to that; but we might not have.)
18711 */
18713 }
18715 }
18716
18718}
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 15285 of file tablecmds.c.

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

15527{
15528 if (!get_index_isclustered(indoid))
15529 return;
15530
15531 if (tab->clusterOnIndex)
15532 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15533
15534 tab->clusterOnIndex = get_rel_name(indoid);
15535}
bool get_index_isclustered(Oid index_oid)
Definition lsyscache.c:3823

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

15543{
15544 /*
15545 * This de-duplication check is critical for two independent reasons: we
15546 * mustn't try to recreate the same constraint twice, and if a constraint
15547 * depends on more than one column whose type is to be altered, we must
15548 * capture its definition string before applying any of the column type
15549 * changes. ruleutils.c will get confused if we ask again later.
15550 */
15551 if (!list_member_oid(tab->changedConstraintOids, conoid))
15552 {
15553 /* OK, capture the constraint's existing definition string */
15555 Oid indoid;
15556
15557 /*
15558 * It is critical to create not-null constraints ahead of primary key
15559 * indexes; otherwise, the not-null constraint would be created by the
15560 * primary key, and the constraint name would be wrong.
15561 */
15563 {
15564 tab->changedConstraintOids = lcons_oid(conoid,
15568 }
15569 else
15570 {
15571
15573 conoid);
15575 defstring);
15576 }
15577
15578 /*
15579 * For the index of a constraint, if any, remember if it is used for
15580 * the table's replica identity or if it is a clustered index, so that
15581 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15582 * those properties.
15583 */
15584 indoid = get_constraint_index(conoid);
15585 if (OidIsValid(indoid))
15586 {
15588 RememberClusterOnForRebuilding(indoid, tab);
15589 }
15590 }
15591}
List * lcons_oid(Oid datum, List *list)
Definition list.c:531
char * pg_get_constraintdef_command(Oid constraintId)
Definition ruleutils.c:2540
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 15598 of file tablecmds.c.

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

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

15512{
15513 if (!get_index_isreplident(indoid))
15514 return;
15515
15516 if (tab->replicaIdentityIndex)
15517 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15518
15519 tab->replicaIdentityIndex = get_rel_name(indoid);
15520}
bool get_index_isreplident(Oid index_oid)
Definition lsyscache.c:3777

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

15650{
15651 /*
15652 * This de-duplication check is critical for two independent reasons: we
15653 * mustn't try to recreate the same statistics object twice, and if the
15654 * statistics object depends on more than one column whose type is to be
15655 * altered, we must capture its definition string before applying any of
15656 * the type changes. ruleutils.c will get confused if we ask again later.
15657 */
15659 {
15660 /* OK, capture the statistics object's existing definition string */
15662
15664 stxoid);
15666 defstring);
15667 }
15668}
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition ruleutils.c:1983

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

19532{
19533 ListCell *l;
19534
19535 foreach(l, on_commits)
19536 {
19538
19539 if (oc->relid == relid)
19540 {
19542 break;
19543 }
19544 }
19545}
SubTransactionId deleting_subid
Definition tablecmds.c:130

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

18185{
18187 SysScanDesc scan;
18188 ScanKeyData key[3];
18191 AttrMap *attmap;
18192 List *connames;
18193 List *nncolumns;
18194 bool found;
18195 bool is_partitioning;
18196
18198
18203 if (!found)
18204 {
18205 if (is_partitioning)
18206 ereport(ERROR,
18208 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18211 else
18212 ereport(ERROR,
18214 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18217 }
18218
18219 /*
18220 * Search through child columns looking for ones matching parent rel
18221 */
18223 ScanKeyInit(&key[0],
18228 true, NULL, 1, key);
18230 {
18232
18233 /* Ignore if dropped or not inherited */
18234 if (att->attisdropped)
18235 continue;
18236 if (att->attinhcount <= 0)
18237 continue;
18238
18240 NameStr(att->attname)))
18241 {
18242 /* Decrement inhcount and possibly set islocal to true */
18245
18246 copy_att->attinhcount--;
18247 if (copy_att->attinhcount == 0)
18248 copy_att->attislocal = true;
18249
18252 }
18253 }
18254 systable_endscan(scan);
18256
18257 /*
18258 * Likewise, find inherited check and not-null constraints and disinherit
18259 * them. To do this, we first need a list of the names of the parent's
18260 * check constraints. (We cheat a bit by only checking for name matches,
18261 * assuming that the expressions will match.)
18262 *
18263 * For NOT NULL columns, we store column numbers to match, mapping them in
18264 * to the child rel's attribute numbers.
18265 */
18268 false);
18269
18271 ScanKeyInit(&key[0],
18276 true, NULL, 1, key);
18277
18278 connames = NIL;
18279 nncolumns = NIL;
18280
18282 {
18284
18285 if (con->connoinherit)
18286 continue;
18287
18288 if (con->contype == CONSTRAINT_CHECK)
18289 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18290 if (con->contype == CONSTRAINT_NOTNULL)
18291 {
18293
18295 }
18296 }
18297
18298 systable_endscan(scan);
18299
18300 /* Now scan the child's constraints to find matches */
18301 ScanKeyInit(&key[0],
18306 true, NULL, 1, key);
18307
18309 {
18311 bool match = false;
18312
18313 /*
18314 * Match CHECK constraints by name, not-null constraints by column
18315 * number, and ignore all others.
18316 */
18317 if (con->contype == CONSTRAINT_CHECK)
18318 {
18320 {
18321 if (con->contype == CONSTRAINT_CHECK &&
18322 strcmp(NameStr(con->conname), chkname) == 0)
18323 {
18324 match = true;
18326 break;
18327 }
18328 }
18329 }
18330 else if (con->contype == CONSTRAINT_NOTNULL)
18331 {
18333
18335 {
18336 if (prevattno == child_attno)
18337 {
18338 match = true;
18340 break;
18341 }
18342 }
18343 }
18344 else
18345 continue;
18346
18347 if (match)
18348 {
18349 /* Decrement inhcount and possibly set islocal to true */
18352
18353 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18354 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18356
18357 copy_con->coninhcount--;
18358 if (copy_con->coninhcount == 0)
18359 copy_con->conislocal = true;
18360
18363 }
18364 }
18365
18366 /* We should have matched all constraints */
18367 if (connames != NIL || nncolumns != NIL)
18368 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18371
18372 systable_endscan(scan);
18374
18379
18380 /*
18381 * Post alter hook of this inherits. Since object_access_hook doesn't take
18382 * multiple object identifiers, we relay oid of parent relation using
18383 * auxiliary_id argument.
18384 */
18388}
bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending, const char *childname)
bool SearchSysCacheExistsAttName(Oid relid, const char *attname)
Definition syscache.c:517
#define child_dependency_type(child_is_partition)
Definition tablecmds.c:373

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

11998{
12002 SysScanDesc scan;
12004
12005 ScanKeyInit(&key,
12008 ObjectIdGetDatum(conrelid));
12009
12012 true, NULL, 1, &key);
12014 while ((consttup = systable_getnext(scan)) != NULL)
12015 {
12017
12018 if (conform->conparentid != conoid)
12019 continue;
12020 else
12021 {
12022 ObjectAddress addr;
12026
12029
12030 /*
12031 * First we must delete the dependency record that binds the
12032 * constraint records together.
12033 */
12035 conform->oid,
12038 conoid);
12039 Assert(n == 1); /* actually only one is expected */
12040
12041 /*
12042 * Now search for the triggers for this constraint and set them up
12043 * for deletion too
12044 */
12050 true, NULL, 1, &key2);
12051 while ((trigtup = systable_getnext(scan2)) != NULL)
12052 {
12056 }
12058 }
12059 }
12060 /* make the dependency deletions visible */
12064 systable_endscan(scan);
12065}
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:243
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 1563 of file tablecmds.c.

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

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

4085{
4088 HeapTuple tuple;
4090 ObjectAddress address;
4091
4092 Assert(!myrelid || !mytypid);
4093
4094 if (mytypid)
4095 {
4097 }
4098 else
4099 {
4101
4102 /*
4103 * don't tell it whether we're recursing; we allow changing typed
4104 * tables here
4105 */
4107
4109 }
4110
4112 if (!HeapTupleIsValid(tuple))
4113 elog(ERROR, "cache lookup failed for constraint %u",
4115 con = (Form_pg_constraint) GETSTRUCT(tuple);
4116
4117 if (myrelid &&
4118 (con->contype == CONSTRAINT_CHECK ||
4119 con->contype == CONSTRAINT_NOTNULL) &&
4120 !con->connoinherit)
4121 {
4122 if (recurse)
4123 {
4126 ListCell *lo,
4127 *li;
4128
4131
4133 {
4135 int numparents = lfirst_int(li);
4136
4137 if (childrelid == myrelid)
4138 continue;
4139
4141 }
4142 }
4143 else
4144 {
4145 if (expected_parents == 0 &&
4147 ereport(ERROR,
4149 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4150 oldconname)));
4151 }
4152
4153 if (con->coninhcount > expected_parents)
4154 ereport(ERROR,
4156 errmsg("cannot rename inherited constraint \"%s\"",
4157 oldconname)));
4158 }
4159
4160 if (con->conindid
4161 && (con->contype == CONSTRAINT_PRIMARY
4162 || con->contype == CONSTRAINT_UNIQUE
4163 || con->contype == CONSTRAINT_EXCLUSION))
4164 /* rename the index; this renames the constraint as well */
4165 RenameRelationInternal(con->conindid, newconname, false, true);
4166 else
4168
4170
4171 ReleaseSysCache(tuple);
4172
4173 if (targetrelation)
4174 {
4175 /*
4176 * Invalidate relcache so as others can see the new constraint name.
4177 */
4179
4180 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4181 }
4182
4183 return address;
4184}
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:4078

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

4041{
4042 Oid relid;
4044 ObjectAddress address;
4045
4046 /* lock level taken here should match renameatt_internal */
4048 stmt->missing_ok ? RVR_MISSING_OK : 0,
4050 NULL);
4051
4052 if (!OidIsValid(relid))
4053 {
4055 (errmsg("relation \"%s\" does not exist, skipping",
4056 stmt->relation->relname)));
4057 return InvalidObjectAddress;
4058 }
4059
4060 attnum =
4061 renameatt_internal(relid,
4062 stmt->subname, /* old att name */
4063 stmt->newname, /* new att name */
4064 stmt->relation->inh, /* recursive? */
4065 false, /* recursing? */
4066 0, /* expected inhcount */
4067 stmt->behavior);
4068
4070
4071 return address;
4072}
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition tablecmds.c:3875
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition tablecmds.c:4020

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

3827{
3828 char relkind = classform->relkind;
3829
3830 if (classform->reloftype && !recursing)
3831 ereport(ERROR,
3833 errmsg("cannot rename column of typed table")));
3834
3835 /*
3836 * Renaming the columns of sequences or toast tables doesn't actually
3837 * break anything from the system's point of view, since internal
3838 * references are by attnum. But it doesn't seem right to allow users to
3839 * change names that are hardcoded into the system, hence the following
3840 * restriction.
3841 */
3842 if (relkind != RELKIND_RELATION &&
3843 relkind != RELKIND_VIEW &&
3844 relkind != RELKIND_MATVIEW &&
3845 relkind != RELKIND_COMPOSITE_TYPE &&
3846 relkind != RELKIND_INDEX &&
3847 relkind != RELKIND_PARTITIONED_INDEX &&
3848 relkind != RELKIND_FOREIGN_TABLE &&
3849 relkind != RELKIND_PARTITIONED_TABLE)
3850 ereport(ERROR,
3852 errmsg("cannot rename columns of relation \"%s\"",
3853 NameStr(classform->relname)),
3855
3856 /*
3857 * permissions checking. only the owner of a class can change its schema.
3858 */
3861 NameStr(classform->relname));
3863 ereport(ERROR,
3865 errmsg("permission denied: \"%s\" is a system catalog",
3866 NameStr(classform->relname))));
3867}

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

3882{
3888
3889 /*
3890 * Grab an exclusive lock on the target table, which we will NOT release
3891 * until end of transaction.
3892 */
3895
3896 /*
3897 * if the 'recurse' flag is set then we are supposed to rename this
3898 * attribute in all classes that inherit from 'relname' (as well as in
3899 * 'relname').
3900 *
3901 * any permissions or problems with duplicate attributes will cause the
3902 * whole transaction to abort, which is what we want -- all or nothing.
3903 */
3904 if (recurse)
3905 {
3908 ListCell *lo,
3909 *li;
3910
3911 /*
3912 * we need the number of parents for each child so that the recursive
3913 * calls to renameatt() can determine whether there are any parents
3914 * outside the inheritance hierarchy being processed.
3915 */
3918
3919 /*
3920 * find_all_inheritors does the recursive search of the inheritance
3921 * hierarchy, so all we have to do is process all of the relids in the
3922 * list that it returns.
3923 */
3925 {
3927 int numparents = lfirst_int(li);
3928
3929 if (childrelid == myrelid)
3930 continue;
3931 /* note we need not recurse again */
3933 }
3934 }
3935 else
3936 {
3937 /*
3938 * If we are told not to recurse, there had better not be any child
3939 * tables; else the rename would put them out of step.
3940 *
3941 * expected_parents will only be 0 if we are not already recursing.
3942 */
3943 if (expected_parents == 0 &&
3945 ereport(ERROR,
3947 errmsg("inherited column \"%s\" must be renamed in child tables too",
3948 oldattname)));
3949 }
3950
3951 /* rename attributes in typed tables of composite type */
3952 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
3953 {
3955 ListCell *lo;
3956
3959 behavior);
3960
3961 foreach(lo, child_oids)
3962 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
3963 }
3964
3966
3969 ereport(ERROR,
3971 errmsg("column \"%s\" does not exist",
3972 oldattname)));
3974
3975 attnum = attform->attnum;
3976 if (attnum <= 0)
3977 ereport(ERROR,
3979 errmsg("cannot rename system column \"%s\"",
3980 oldattname)));
3981
3982 /*
3983 * if the attribute is inherited, forbid the renaming. if this is a
3984 * top-level call to renameatt(), then expected_parents will be 0, so the
3985 * effect of this code will be to prohibit the renaming if the attribute
3986 * is inherited at all. if this is a recursive call to renameatt(),
3987 * expected_parents will be the number of parents the current relation has
3988 * within the inheritance hierarchy being processed, so we'll prohibit the
3989 * renaming only if there are additional parents from elsewhere.
3990 */
3991 if (attform->attinhcount > expected_parents)
3992 ereport(ERROR,
3994 errmsg("cannot rename inherited column \"%s\"",
3995 oldattname)));
3996
3997 /* new name should not already exist */
3999
4000 /* apply the update */
4001 namestrcpy(&(attform->attname), newattname);
4002
4004
4006
4008
4010
4011 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4012
4013 return attnum;
4014}
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 4187 of file tablecmds.c.

4188{
4189 Oid relid = InvalidOid;
4190 Oid typid = InvalidOid;
4191
4192 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4193 {
4194 Relation rel;
4195 HeapTuple tup;
4196
4200 if (!HeapTupleIsValid(tup))
4201 elog(ERROR, "cache lookup failed for type %u", typid);
4204 table_close(rel, NoLock);
4205 }
4206 else
4207 {
4208 /* lock level taken here should match rename_constraint_internal */
4210 stmt->missing_ok ? RVR_MISSING_OK : 0,
4212 NULL);
4213 if (!OidIsValid(relid))
4214 {
4216 (errmsg("relation \"%s\" does not exist, skipping",
4217 stmt->relation->relname)));
4218 return InvalidObjectAddress;
4219 }
4220 }
4221
4222 return
4223 rename_constraint_internal(relid, typid,
4224 stmt->subname,
4225 stmt->newname,
4226 (stmt->relation &&
4227 stmt->relation->inh), /* recursive? */
4228 false, /* recursing? */
4229 0 /* expected inhcount */ );
4230}
void checkDomainOwner(HeapTuple tup)
Definition typecmds.c:3527

References AccessExclusiveLock, castNode, checkDomainOwner(), elog, ereport, errmsg, ERROR, fb(), HeapTupleIsValid, InvalidObjectAddress, InvalidOid, makeTypeNameFromNameList(), NoLock, NOTICE, OBJECT_DOMCONSTRAINT, ObjectIdGetDatum(), OidIsValid, RangeVarCallbackForRenameAttribute(), RangeVarGetRelidExtended(), ReleaseSysCache(), rename_constraint_internal(), RowExclusiveLock, RVR_MISSING_OK, SearchSysCache1(), stmt, table_close(), table_open(), and typenameTypeId().

Referenced by ExecRenameStmt().

◆ RenameRelation()

ObjectAddress RenameRelation ( RenameStmt stmt)

Definition at line 4237 of file tablecmds.c.

4238{
4239 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4240 Oid relid;
4241 ObjectAddress address;
4242
4243 /*
4244 * Grab an exclusive lock on the target table, index, sequence, view,
4245 * materialized view, or foreign table, which we will NOT release until
4246 * end of transaction.
4247 *
4248 * Lock level used here should match RenameRelationInternal, to avoid lock
4249 * escalation. However, because ALTER INDEX can be used with any relation
4250 * type, we mustn't believe without verification.
4251 */
4252 for (;;)
4253 {
4254 LOCKMODE lockmode;
4255 char relkind;
4256 bool obj_is_index;
4257
4259
4260 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4261 stmt->missing_ok ? RVR_MISSING_OK : 0,
4263 stmt);
4264
4265 if (!OidIsValid(relid))
4266 {
4268 (errmsg("relation \"%s\" does not exist, skipping",
4269 stmt->relation->relname)));
4270 return InvalidObjectAddress;
4271 }
4272
4273 /*
4274 * We allow mismatched statement and object types (e.g., ALTER INDEX
4275 * to rename a table), but we might've used the wrong lock level. If
4276 * that happens, retry with the correct lock level. We don't bother
4277 * if we already acquired AccessExclusiveLock with an index, however.
4278 */
4279 relkind = get_rel_relkind(relid);
4280 obj_is_index = (relkind == RELKIND_INDEX ||
4281 relkind == RELKIND_PARTITIONED_INDEX);
4283 break;
4284
4285 UnlockRelationOid(relid, lockmode);
4287 }
4288
4289 /* Do the work */
4290 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4291
4292 ObjectAddressSet(address, RelationRelationId, relid);
4293
4294 return address;
4295}

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

4302{
4304 Relation relrelation; /* for RELATION relation */
4309
4310 /*
4311 * Grab a lock on the target relation, which we will NOT release until end
4312 * of transaction. We need at least a self-exclusive lock so that
4313 * concurrent DDL doesn't overwrite the rename if they start updating
4314 * while still seeing the old version. The lock also guards against
4315 * triggering relcache reloads in concurrent sessions, which might not
4316 * handle this information changing under them. For indexes, we can use a
4317 * reduced lock level because RelationReloadIndexInfo() handles indexes
4318 * specially.
4319 */
4322
4323 /*
4324 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4325 */
4327
4329 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4330 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4331 otid = reltup->t_self;
4333
4335 ereport(ERROR,
4337 errmsg("relation \"%s\" already exists",
4338 newrelname)));
4339
4340 /*
4341 * RenameRelation is careful not to believe the caller's idea of the
4342 * relation kind being handled. We don't have to worry about this, but
4343 * let's not be totally oblivious to it. We can process an index as
4344 * not-an-index, but not the other way around.
4345 */
4346 Assert(!is_index ||
4347 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4348 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4349
4350 /*
4351 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4352 * because it's a copy...)
4353 */
4354 namestrcpy(&(relform->relname), newrelname);
4355
4358
4360 InvalidOid, is_internal);
4361
4364
4365 /*
4366 * Also rename the associated type, if any.
4367 */
4368 if (OidIsValid(targetrelation->rd_rel->reltype))
4369 RenameTypeInternal(targetrelation->rd_rel->reltype,
4371
4372 /*
4373 * Also rename the associated constraint, if any.
4374 */
4375 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4376 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4377 {
4379
4382 }
4383
4384 /*
4385 * Close rel, but keep lock!
4386 */
4388}
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 4394 of file tablecmds.c.

4395{
4396 Relation relrelation; /* for RELATION relation */
4399
4400 /*
4401 * Find relation's pg_class tuple.
4402 */
4404
4406 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4407 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4409
4410 /*
4411 * Update pg_class tuple.
4412 */
4413 relform->relrewrite = InvalidOid;
4414
4416
4419}

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

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

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

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

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

3679{
3681 HeapTuple tuple;
3683
3685 ShareUpdateExclusiveLock, false) ||
3687 ShareRowExclusiveLock, true));
3688
3689 /*
3690 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3691 */
3694 if (!HeapTupleIsValid(tuple))
3695 elog(ERROR, "cache lookup failed for relation %u", relationId);
3697
3698 if (classtuple->relhassubclass != relhassubclass)
3699 {
3700 classtuple->relhassubclass = relhassubclass;
3702 }
3703 else
3704 {
3705 /* no need to change tuple, but force relcache rebuild anyway */
3707 }
3708
3709 heap_freetuple(tuple);
3711}
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 3781 of file tablecmds.c.

3784{
3786 HeapTuple tuple;
3788 Form_pg_class rd_rel;
3789 Oid reloid = RelationGetRelid(rel);
3790
3792
3793 /* Get a modifiable copy of the relation's pg_class row. */
3795
3797 if (!HeapTupleIsValid(tuple))
3798 elog(ERROR, "cache lookup failed for relation %u", reloid);
3799 otid = tuple->t_self;
3800 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3801
3802 /* Update the pg_class row. */
3803 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3806 rd_rel->relfilenode = newRelFilenumber;
3809
3810 /*
3811 * Record dependency on tablespace. This is only required for relations
3812 * that have no physical storage.
3813 */
3814 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3816 rd_rel->reltablespace);
3817
3818 heap_freetuple(tuple);
3820}
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 23263 of file tablecmds.c.

23265{
23266 /* The FSM is empty, so don't bother using it. */
23267 int ti_options = TABLE_INSERT_SKIP_FSM;
23268 CommandId mycid;
23269 EState *estate;
23271 *listptr2;
23273 ExprContext *econtext;
23274 TableScanDesc scan;
23275 Snapshot snapshot;
23280 *pc;
23281
23282 mycid = GetCurrentCommandId(true);
23283
23284 estate = CreateExecutorState();
23285
23286 forboth(listptr, partlist, listptr2, newPartRels)
23287 {
23289
23291
23292 /* Find the work queue entry for the new partition table: newPartRel. */
23293 pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
23294
23295 buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
23296
23297 if (sps->bound->is_default)
23298 {
23299 /*
23300 * We should not create a structure to check the partition
23301 * constraint for the new DEFAULT partition.
23302 */
23304 }
23305 else
23306 {
23308
23309 /* Build expression execution states for partition check quals. */
23313 (Node *) partConstraint);
23314 /* Make a boolean expression for ExecCheck(). */
23316
23317 /*
23318 * Map the vars in the constraint expression from rel's attnos to
23319 * splitRel's.
23320 */
23322 1, splitRel, rel);
23323
23324 pc->partqualstate =
23326 Assert(pc->partqualstate != NULL);
23327 }
23328
23329 /* Store partition context into a list. */
23331 }
23332
23333 econtext = GetPerTupleExprContext(estate);
23334
23335 /* Create the necessary tuple slot. */
23337
23338 /*
23339 * Map computing for moving attributes of the split partition to the new
23340 * partition (for the first new partition, but other new partitions can
23341 * use the same map).
23342 */
23345 RelationGetDescr(pc->partRel));
23346
23347 /* Scan through the rows. */
23348 snapshot = RegisterSnapshot(GetLatestSnapshot());
23349 scan = table_beginscan(splitRel, snapshot, 0, NULL);
23350
23351 /*
23352 * Switch to per-tuple memory context and reset it for each tuple
23353 * produced, so we don't leak memory.
23354 */
23356
23358 {
23359 bool found = false;
23361
23363
23364 econtext->ecxt_scantuple = srcslot;
23365
23366 /* Search partition for the current slot, srcslot. */
23367 foreach(listptr, partContexts)
23368 {
23370
23371 /* skip DEFAULT partition */
23372 if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
23373 {
23374 found = true;
23375 break;
23376 }
23377 }
23378 if (!found)
23379 {
23380 /* Use the DEFAULT partition if it exists. */
23381 if (defaultPartCtx)
23383 else
23384 ereport(ERROR,
23386 errmsg("can not find partition for split partition row"),
23388 }
23389
23390 if (tuple_map)
23391 {
23392 /* Need to use a map to copy attributes. */
23393 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
23394 }
23395 else
23396 {
23397 /* Extract data from the old tuple. */
23399
23400 /* Copy attributes directly. */
23401 insertslot = pc->dstslot;
23402
23404
23405 memcpy(insertslot->tts_values, srcslot->tts_values,
23406 sizeof(Datum) * srcslot->tts_nvalid);
23407 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23408 sizeof(bool) * srcslot->tts_nvalid);
23409
23411 }
23412
23413 /*
23414 * Constraints and GENERATED expressions might reference the tableoid
23415 * column, so fill tts_tableOid with the desired value. (We must do
23416 * this each time, because it gets overwritten with newrel's OID
23417 * during storing.)
23418 */
23419 insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
23420
23421 /*
23422 * Now, evaluate any generated expressions whose inputs come from the
23423 * new tuple. We assume these columns won't reference each other, so
23424 * that there's no ordering dependency.
23425 */
23427 insertslot, econtext);
23428
23429 /* Write the tuple out to the new relation. */
23430 table_tuple_insert(pc->partRel, insertslot, mycid,
23431 ti_options, pc->bistate);
23432
23433 ResetExprContext(econtext);
23434 }
23435
23437
23438 table_endscan(scan);
23439 UnregisterSnapshot(snapshot);
23440
23441 if (tuple_map)
23443
23445
23446 FreeExecutorState(estate);
23447
23450}
static SplitPartitionContext * createSplitPartitionContext(Relation partRel)
static void deleteSplitPartitionContext(SplitPartitionContext *pc, List **wqueue, int ti_options)

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

Referenced by ATExecSplitPartition().

◆ storage_name()

static const char * storage_name ( char  c)
static

Definition at line 2493 of file tablecmds.c.

2494{
2495 switch (c)
2496 {
2497 case TYPSTORAGE_PLAIN:
2498 return "PLAIN";
2500 return "EXTERNAL";
2502 return "EXTENDED";
2503 case TYPSTORAGE_MAIN:
2504 return "MAIN";
2505 default:
2506 return "???";
2507 }
2508}
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 3552 of file tablecmds.c.

3554{
3555 Relation relation;
3557 ListCell *entry;
3558
3559 /*
3560 * sanity checks
3561 */
3563
3564 if (supers == NIL)
3565 return;
3566
3567 /*
3568 * Store INHERITS information in pg_inherits using direct ancestors only.
3569 * Also enter dependencies on the direct ancestors, and make sure they are
3570 * marked with relhassubclass = true.
3571 *
3572 * (Once upon a time, both direct and indirect ancestors were found here
3573 * and then entered into pg_ipl. Since that catalog doesn't exist
3574 * anymore, there's no need to look for indirect ancestors.)
3575 */
3577
3578 seqNumber = 1;
3579 foreach(entry, supers)
3580 {
3581 Oid parentOid = lfirst_oid(entry);
3582
3585 seqNumber++;
3586 }
3587
3588 table_close(relation, RowExclusiveLock);
3589}

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

3599{
3602
3603 /* store the pg_inherits row */
3605
3606 /*
3607 * Store a dependency too
3608 */
3610 parentobject.objectId = parentOid;
3611 parentobject.objectSubId = 0;
3613 childobject.objectId = relationId;
3614 childobject.objectSubId = 0;
3615
3618
3619 /*
3620 * Post creation hook of this inheritance. Since object_access_hook
3621 * doesn't take multiple object identifiers, we relay oid of parent
3622 * relation using auxiliary_id argument.
3623 */
3625 relationId, 0,
3626 parentOid, false);
3627
3628 /*
3629 * Mark the parent as having subclasses.
3630 */
3632}
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition tablecmds.c:3678

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

13571{
13572 ListCell *l;
13573 int attnum;
13574
13575 attnum = 0;
13576 foreach(l, colList)
13577 {
13578 char *attname = strVal(lfirst(l));
13581
13584 ereport(ERROR,
13586 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13587 attname)));
13589 if (attform->attnum < 0)
13590 ereport(ERROR,
13592 errmsg("system columns cannot be used in foreign keys")));
13593 if (attnum >= INDEX_MAX_KEYS)
13594 ereport(ERROR,
13596 errmsg("cannot have more than %d keys in a foreign key",
13597 INDEX_MAX_KEYS)));
13598 attnums[attnum] = attform->attnum;
13599 if (atttypids != NULL)
13600 atttypids[attnum] = attform->atttypid;
13601 if (attcollids != NULL)
13602 attcollids[attnum] = attform->attcollation;
13604 attnum++;
13605 }
13606
13607 return attnum;
13608}

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

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

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

13628{
13635 int i;
13636
13637 /*
13638 * Get the list of index OIDs for the table from the relcache, and look up
13639 * each one in the pg_index syscache until we find one marked primary key
13640 * (hopefully there isn't more than one such). Insist it's valid, too.
13641 */
13642 *indexOid = InvalidOid;
13643
13645
13646 foreach(indexoidscan, indexoidlist)
13647 {
13648 Oid indexoid = lfirst_oid(indexoidscan);
13649
13652 elog(ERROR, "cache lookup failed for index %u", indexoid);
13654 if (indexStruct->indisprimary && indexStruct->indisvalid)
13655 {
13656 /*
13657 * Refuse to use a deferrable primary key. This is per SQL spec,
13658 * and there would be a lot of interesting semantic problems if we
13659 * tried to allow it.
13660 */
13661 if (!indexStruct->indimmediate)
13662 ereport(ERROR,
13664 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13666
13667 *indexOid = indexoid;
13668 break;
13669 }
13671 }
13672
13674
13675 /*
13676 * Check that we found it
13677 */
13678 if (!OidIsValid(*indexOid))
13679 ereport(ERROR,
13681 errmsg("there is no primary key for referenced table \"%s\"",
13683
13684 /* Must get indclass the hard way */
13688
13689 /*
13690 * Now build the list of PK attributes from the indkey definition (we
13691 * assume a primary key cannot have expressional elements)
13692 */
13693 *attnamelist = NIL;
13694 for (i = 0; i < indexStruct->indnkeyatts; i++)
13695 {
13696 int pkattno = indexStruct->indkey.values[i];
13697
13698 attnums[i] = pkattno;
13701 opclasses[i] = indclass->values[i];
13704 }
13705
13706 *pk_has_without_overlaps = indexStruct->indisexclusion;
13707
13709
13710 return i;
13711}
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 19966 of file tablecmds.c.

19967{
19969 ParseState *pstate;
19971 ListCell *l;
19972
19974
19975 newspec->strategy = partspec->strategy;
19976 newspec->partParams = NIL;
19977 newspec->location = partspec->location;
19978
19979 /* Check valid number of columns for strategy */
19980 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
19981 list_length(partspec->partParams) != 1)
19982 ereport(ERROR,
19984 errmsg("cannot use \"list\" partition strategy with more than one column")));
19985
19986 /*
19987 * Create a dummy ParseState and insert the target relation as its sole
19988 * rangetable entry. We need a ParseState for transformExpr.
19989 */
19990 pstate = make_parsestate(NULL);
19992 NULL, false, true);
19993 addNSItemToQuery(pstate, nsitem, true, true, true);
19994
19995 /* take care of any partition expressions */
19996 foreach(l, partspec->partParams)
19997 {
19999
20000 if (pelem->expr)
20001 {
20002 /* Copy, to avoid scribbling on the input */
20004
20005 /* Now do parse transformation of the expression */
20006 pelem->expr = transformExpr(pstate, pelem->expr,
20008
20009 /* we have to fix its collations too */
20010 assign_expr_collations(pstate, pelem->expr);
20011 }
20012
20013 newspec->partParams = lappend(newspec->partParams, pelem);
20014 }
20015
20016 return newspec;
20017}
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition parse_expr.c:121
@ EXPR_KIND_PARTITION_EXPRESSION
Definition parse_node.h:80
@ PARTITION_STRATEGY_LIST
Definition parsenodes.h:914
List * partParams
Definition parsenodes.h:928
ParseLoc location
Definition parsenodes.h:929
PartitionStrategy strategy
Definition parsenodes.h:927

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

2471{
2472 /*
2473 * Don't allow truncate on temp tables of other backends ... their local
2474 * buffer manager is not going to cope.
2475 */
2476 if (RELATION_IS_OTHER_TEMP(rel))
2477 ereport(ERROR,
2479 errmsg("cannot truncate temporary tables of other sessions")));
2480
2481 /*
2482 * Also check for active uses of the relation in the current transaction,
2483 * including open scans and pending AFTER trigger events.
2484 */
2485 CheckTableNotInUse(rel, "TRUNCATE");
2486}

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

2453{
2454 char *relname = NameStr(reltuple->relname);
2456
2457 /* Permissions checks */
2459 if (aclresult != ACLCHECK_OK)
2461 relname);
2462}
#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 2401 of file tablecmds.c.

2402{
2403 char *relname = NameStr(reltuple->relname);
2404
2405 /*
2406 * Only allow truncate on regular tables, foreign tables using foreign
2407 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2408 * latter are only being included here for the following checks; no
2409 * physical truncation will occur in their case.).
2410 */
2411 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2412 {
2413 Oid serverid = GetForeignServerIdByRelId(relid);
2414 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2415
2416 if (!fdwroutine->ExecForeignTruncate)
2417 ereport(ERROR,
2419 errmsg("cannot truncate foreign table \"%s\"",
2420 relname)));
2421 }
2422 else if (reltuple->relkind != RELKIND_RELATION &&
2424 ereport(ERROR,
2426 errmsg("\"%s\" is not a table", relname)));
2427
2428 /*
2429 * Most system catalogs can't be truncated at all, or at least not unless
2430 * allow_system_table_mods=on. As an exception, however, we allow
2431 * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2432 * pg_upgrade, because we need to change its relfilenode to match the old
2433 * cluster, and allowing a TRUNCATE command to be executed is the easiest
2434 * way of doing that.
2435 */
2437 && (!IsBinaryUpgrade ||
2438 (relid != LargeObjectRelationId &&
2440 ereport(ERROR,
2442 errmsg("permission denied: \"%s\" is a system catalog",
2443 relname)));
2444
2446}
bool IsBinaryUpgrade
Definition globals.c:121
#define InvokeObjectTruncateHook(objectId)

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

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

◆ tryAttachPartitionForeignKey()

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

Definition at line 11769 of file tablecmds.c.

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

16158{
16159 HeapTuple tup;
16160 Datum adatum;
16161 ArrayType *arr;
16162 Oid *rawarr;
16163 int numkeys;
16164 int i;
16165
16166 Assert(con->contype == CONSTR_FOREIGN);
16167 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
16168
16170 if (!HeapTupleIsValid(tup)) /* should not happen */
16171 elog(ERROR, "cache lookup failed for constraint %u", oldId);
16172
16175 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
16176 numkeys = ARR_DIMS(arr)[0];
16177 /* test follows the one in ri_FetchConstraintInfo() */
16178 if (ARR_NDIM(arr) != 1 ||
16179 ARR_HASNULL(arr) ||
16180 ARR_ELEMTYPE(arr) != OIDOID)
16181 elog(ERROR, "conpfeqop is not a 1-D Oid array");
16182 rawarr = (Oid *) ARR_DATA_PTR(arr);
16183
16184 /* stash a List of the operator Oids in our Constraint node */
16185 for (i = 0; i < numkeys; i++)
16187
16189}
#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 16128 of file tablecmds.c.

16129{
16131 stmt->accessMethod,
16132 stmt->indexParams,
16133 stmt->excludeOpNames,
16134 stmt->iswithoutoverlaps))
16135 {
16137
16138 /* If it's a partitioned index, there is no storage to share. */
16139 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
16140 {
16141 stmt->oldNumber = irel->rd_locator.relNumber;
16142 stmt->oldCreateSubid = irel->rd_createSubid;
16143 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
16144 }
16145 index_close(irel, NoLock);
16146 }
16147}
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 10721 of file tablecmds.c.

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

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

13942{
13943 TupleTableSlot *slot;
13944 TableScanDesc scan;
13945 Trigger trig = {0};
13946 Snapshot snapshot;
13949
13951 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
13952
13953 /*
13954 * Build a trigger call structure; we'll need it either way.
13955 */
13956 trig.tgoid = InvalidOid;
13957 trig.tgname = conname;
13958 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
13959 trig.tgisinternal = true;
13960 trig.tgconstrrelid = RelationGetRelid(pkrel);
13961 trig.tgconstrindid = pkindOid;
13962 trig.tgconstraint = constraintOid;
13963 trig.tgdeferrable = false;
13964 trig.tginitdeferred = false;
13965 /* we needn't fill in remaining fields */
13966
13967 /*
13968 * See if we can do it with a single LEFT JOIN query. A false result
13969 * indicates we must proceed with the fire-the-trigger method. We can't do
13970 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
13971 * left joins.
13972 */
13973 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
13974 return;
13975
13976 /*
13977 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
13978 * if that tuple had just been inserted. If any of those fail, it should
13979 * ereport(ERROR) and that's that.
13980 */
13981 snapshot = RegisterSnapshot(GetLatestSnapshot());
13982 slot = table_slot_create(rel, NULL);
13983 scan = table_beginscan(rel, snapshot, 0, NULL);
13984
13986 "validateForeignKeyConstraint",
13989
13990 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
13991 {
13992 LOCAL_FCINFO(fcinfo, 0);
13993 TriggerData trigdata = {0};
13994
13996
13997 /*
13998 * Make a call to the trigger function
13999 *
14000 * No parameters are passed, but we do set a context
14001 */
14002 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
14003
14004 /*
14005 * We assume RI_FKey_check_ins won't look at flinfo...
14006 */
14007 trigdata.type = T_TriggerData;
14009 trigdata.tg_relation = rel;
14010 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
14011 trigdata.tg_trigslot = slot;
14012 trigdata.tg_trigger = &trig;
14013
14014 fcinfo->context = (Node *) &trigdata;
14015
14016 RI_FKey_check_ins(fcinfo);
14017
14019 }
14020
14023 table_endscan(scan);
14024 UnregisterSnapshot(snapshot);
14026}
#define MemSet(start, val, len)
Definition c.h:1109
HeapTuple ExecFetchSlotHeapTuple(TupleTableSlot *slot, bool materialize, bool *shouldFree)
#define SizeForFunctionCallInfo(nargs)
Definition fmgr.h:102
#define LOCAL_FCINFO(name, nargs)
Definition fmgr.h:110
Datum RI_FKey_check_ins(PG_FUNCTION_ARGS)
bool RI_Initial_Check(Trigger *trigger, Relation fk_rel, Relation pk_rel)
NodeTag type
Definition trigger.h:33
Relation tg_relation
Definition trigger.h:35
TriggerEvent tg_event
Definition trigger.h:34
TupleTableSlot * tg_trigslot
Definition trigger.h:39
Trigger * tg_trigger
Definition trigger.h:38
HeapTuple tg_trigtuple
Definition trigger.h:36
#define TRIGGER_EVENT_ROW
Definition trigger.h:100
#define TRIGGER_EVENT_INSERT
Definition trigger.h:94

References ALLOCSET_SMALL_SIZES, AllocSetContextCreate, CHECK_FOR_INTERRUPTS, CurrentMemoryContext, DEBUG1, ereport, errmsg_internal(), ExecDropSingleTupleTableSlot(), ExecFetchSlotHeapTuple(), fb(), ForwardScanDirection, GetLatestSnapshot(), InvalidOid, LOCAL_FCINFO, MemoryContextDelete(), MemoryContextReset(), MemoryContextSwitchTo(), MemSet, RegisterSnapshot(), RelationGetRelid, RI_FKey_check_ins(), RI_Initial_Check(), SizeForFunctionCallInfo, table_beginscan(), table_endscan(), table_scan_getnextslot(), table_slot_create(), TriggerData::tg_event, TriggerData::tg_relation, TriggerData::tg_trigger, TriggerData::tg_trigslot, TriggerData::tg_trigtuple, TRIGGER_EVENT_INSERT, TRIGGER_EVENT_ROW, TRIGGER_FIRES_ON_ORIGIN, TriggerData::type, and UnregisterSnapshot().

Referenced by ATRewriteTables().

◆ validatePartitionedIndex()

static void validatePartitionedIndex ( Relation  partedIdx,
Relation  partedTbl 
)
static

Definition at line 22067 of file tablecmds.c.

22068{
22070 SysScanDesc scan;
22072 int tuples = 0;
22074 bool updated = false;
22075
22076 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
22077
22078 /*
22079 * Scan pg_inherits for this parent index. Count each valid index we find
22080 * (verifying the pg_index entry for each), and if we reach the total
22081 * amount we expect, we can mark this parent index as valid.
22082 */
22088 NULL, 1, &key);
22089 while ((inhTup = systable_getnext(scan)) != NULL)
22090 {
22094
22096 ObjectIdGetDatum(inhForm->inhrelid));
22098 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
22100 if (indexForm->indisvalid)
22101 tuples += 1;
22103 }
22104
22105 /* Done with pg_inherits */
22106 systable_endscan(scan);
22108
22109 /*
22110 * If we found as many inherited indexes as the partitioned table has
22111 * partitions, we're good; update pg_index to set indisvalid.
22112 */
22113 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
22114 {
22118
22123 elog(ERROR, "cache lookup failed for index %u",
22126
22127 indexForm->indisvalid = true;
22128 updated = true;
22129
22131
22134 }
22135
22136 /*
22137 * If this index is in turn a partition of a larger index, validating it
22138 * might cause the parent to become valid also. Try that.
22139 */
22140 if (updated && partedIdx->rd_rel->relispartition)
22141 {
22144 Relation parentIdx,
22145 parentTbl;
22146
22147 /* make sure we see the validation we just did */
22149
22154 Assert(!parentIdx->rd_index->indisvalid);
22155
22156 validatePartitionedIndex(parentIdx, parentTbl);
22157
22160 }
22161}

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

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

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

22170{
22171 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
22172 {
22174 iinfo->ii_IndexAttrNumbers[i] - 1);
22175
22176 if (!att->attnotnull)
22177 ereport(ERROR,
22179 errmsg("invalid primary key definition"),
22180 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
22181 NameStr(att->attname),
22183 }
22184}

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

257 {
260 gettext_noop("table \"%s\" does not exist"),
261 gettext_noop("table \"%s\" does not exist, skipping"),
262 gettext_noop("\"%s\" is not a table"),
263 gettext_noop("Use DROP TABLE to remove a table.")},
266 gettext_noop("sequence \"%s\" does not exist"),
267 gettext_noop("sequence \"%s\" does not exist, skipping"),
268 gettext_noop("\"%s\" is not a sequence"),
269 gettext_noop("Use DROP SEQUENCE to remove a sequence.")},
272 gettext_noop("view \"%s\" does not exist"),
273 gettext_noop("view \"%s\" does not exist, skipping"),
274 gettext_noop("\"%s\" is not a view"),
275 gettext_noop("Use DROP VIEW to remove a view.")},
278 gettext_noop("materialized view \"%s\" does not exist"),
279 gettext_noop("materialized view \"%s\" does not exist, skipping"),
280 gettext_noop("\"%s\" is not a materialized view"),
281 gettext_noop("Use DROP MATERIALIZED VIEW to remove a materialized view.")},
284 gettext_noop("index \"%s\" does not exist"),
285 gettext_noop("index \"%s\" does not exist, skipping"),
286 gettext_noop("\"%s\" is not an index"),
287 gettext_noop("Use DROP INDEX to remove an index.")},
290 gettext_noop("type \"%s\" does not exist"),
291 gettext_noop("type \"%s\" does not exist, skipping"),
292 gettext_noop("\"%s\" is not a type"),
293 gettext_noop("Use DROP TYPE to remove a type.")},
296 gettext_noop("foreign table \"%s\" does not exist"),
297 gettext_noop("foreign table \"%s\" does not exist, skipping"),
298 gettext_noop("\"%s\" is not a foreign table"),
299 gettext_noop("Use DROP FOREIGN TABLE to remove a foreign table.")},
302 gettext_noop("table \"%s\" does not exist"),
303 gettext_noop("table \"%s\" does not exist, skipping"),
304 gettext_noop("\"%s\" is not a table"),
305 gettext_noop("Use DROP TABLE to remove a table.")},
308 gettext_noop("index \"%s\" does not exist"),
309 gettext_noop("index \"%s\" does not exist, skipping"),
310 gettext_noop("\"%s\" is not an index"),
311 gettext_noop("Use DROP INDEX to remove an index.")},
314 gettext_noop("property graph \"%s\" does not exist"),
315 gettext_noop("property graph \"%s\" does not exist, skipping"),
316 gettext_noop("\"%s\" is not a property graph"),
317 gettext_noop("Use DROP PROPERTY GRAPH to remove a property graph.")},
318 {'\0', 0, NULL, NULL, NULL, NULL}
319};
#define gettext_noop(x)
Definition c.h:1287

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits