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

Go to the source code of this file.

Data Structures

struct  OnCommitItem
 
struct  AlteredTableInfo
 
struct  NewConstraint
 
struct  NewColumnValue
 
struct  dropmsgstrings
 
struct  DropRelationCallbackState
 
struct  ForeignTruncateInfo
 
struct  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, uint32 ti_options)
 
static void SplitPartitionMoveRows (List **wqueue, Relation rel, Relation splitRel, List *partlist, List *newPartRels)
 

Variables

static Liston_commits = NIL
 
static const struct dropmsgstrings dropmsgstringarray []
 

Macro Definition Documentation

◆ AT_NUM_PASSES

#define AT_NUM_PASSES   (AT_PASS_MISC + 1)

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

7807{
7809 referenced;
7810
7811 /* We know the default collation is pinned, so don't bother recording it */
7813 {
7815 myself.objectId = relid;
7816 myself.objectSubId = attnum;
7818 referenced.objectId = collid;
7819 referenced.objectSubId = 0;
7821 }
7822}
#define OidIsValid(objectId)
Definition c.h:858
Oid collid
int16 attnum
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition pg_depend.c:47
static int fb(int x)

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

Referenced by ATExecAddColumn(), and ATExecAlterColumnType().

◆ add_column_datatype_dependency()

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

Definition at line 7788 of file tablecmds.c.

7789{
7791 referenced;
7792
7794 myself.objectId = relid;
7795 myself.objectSubId = attnum;
7796 referenced.classId = TypeRelationId;
7797 referenced.objectId = typid;
7798 referenced.objectSubId = 0;
7800}

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

10806{
10807 ObjectAddress address;
10808 Oid constrOid;
10809 char *conname;
10810 bool conislocal;
10812 bool connoinherit;
10813
10814 /*
10815 * Verify relkind for each referenced partition. At the top level, this
10816 * is redundant with a previous check, but we need it when recursing.
10817 */
10818 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10819 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10820 ereport(ERROR,
10822 errmsg("referenced relation \"%s\" is not a table",
10824
10825 /*
10826 * Caller supplies us with a constraint name; however, it may be used in
10827 * this partition, so come up with a different one in that case. Unless
10828 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10829 * supplied name with an underscore and digit(s) appended.
10830 */
10832 RelationGetRelid(rel),
10833 constraintname))
10834 conname = ChooseConstraintName(constraintname,
10835 NULL,
10836 "",
10838 else
10839 conname = constraintname;
10840
10841 if (fkconstraint->conname == NULL)
10842 fkconstraint->conname = pstrdup(conname);
10843
10845 {
10846 conislocal = false;
10847 coninhcount = 1;
10848 connoinherit = false;
10849 }
10850 else
10851 {
10852 conislocal = true;
10853 coninhcount = 0;
10854
10855 /*
10856 * always inherit for partitioned tables, never for legacy inheritance
10857 */
10859 }
10860
10861 /*
10862 * Record the FK constraint in pg_constraint.
10863 */
10867 fkconstraint->deferrable,
10868 fkconstraint->initdeferred,
10869 fkconstraint->is_enforced,
10870 fkconstraint->initially_valid,
10872 RelationGetRelid(rel),
10873 fkattnum,
10874 numfks,
10875 numfks,
10876 InvalidOid, /* not a domain constraint */
10877 indexOid,
10879 pkattnum,
10883 numfks,
10884 fkconstraint->fk_upd_action,
10885 fkconstraint->fk_del_action,
10888 fkconstraint->fk_matchtype,
10889 NULL, /* no exclusion constraint */
10890 NULL, /* no check constraint */
10891 NULL,
10892 conislocal, /* islocal */
10893 coninhcount, /* inhcount */
10894 connoinherit, /* conNoInherit */
10895 with_period, /* conPeriod */
10896 is_internal); /* is_internal */
10897
10899
10900 /*
10901 * In partitioning cases, create the dependency entries for this
10902 * constraint. (For non-partitioned cases, relevant entries were created
10903 * by CreateConstraintEntry.)
10904 *
10905 * On the referenced side, we need the constraint to have an internal
10906 * dependency on its parent constraint; this means that this constraint
10907 * cannot be dropped on its own -- only through the parent constraint. It
10908 * also means the containing partition cannot be dropped on its own, but
10909 * it can be detached, at which point this dependency is removed (after
10910 * verifying that no rows are referenced via this FK.)
10911 *
10912 * When processing the referencing side, we link the constraint via the
10913 * special partitioning dependencies: the parent constraint is the primary
10914 * dependent, and the partition on which the foreign key exists is the
10915 * secondary dependency. That way, this constraint is dropped if either
10916 * of these objects is.
10917 *
10918 * Note that this is only necessary for the subsidiary pg_constraint rows
10919 * in partitions; the topmost row doesn't need any of this.
10920 */
10922 {
10924
10926
10930 else
10931 {
10935 }
10936 }
10937
10938 /* make new constraint visible, in case we add more */
10940
10941 return address;
10942}
#define Assert(condition)
Definition c.h:943
int16_t int16
Definition c.h:619
@ DEPENDENCY_INTERNAL
Definition dependency.h:35
@ DEPENDENCY_PARTITION_PRI
Definition dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition dependency.h:37
int errcode(int sqlerrcode)
Definition elog.c:874
#define ERROR
Definition elog.h:40
#define ereport(elevel,...)
Definition elog.h:152
char * pstrdup(const char *in)
Definition mcxt.c:1781
static char * errmsg
#define ObjectAddressSet(addr, class_id, object_id)
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isEnforced, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, const int16 *fkDeleteSetCols, int numFkDeleteSetCols, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int16 conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, const char *conname)
char * ChooseConstraintName(const char *name1, const char *name2, const char *label, Oid namespaceid, List *others)
@ CONSTRAINT_RELATION
#define NIL
Definition pg_list.h:68
#define InvalidOid
unsigned int Oid
#define RelationGetRelid(relation)
Definition rel.h:516
#define RelationGetRelationName(relation)
Definition rel.h:550
#define RelationGetNamespace(relation)
Definition rel.h:557
Form_pg_class rd_rel
Definition rel.h:111
void CommandCounterIncrement(void)
Definition xact.c:1130

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

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

◆ addFkRecurseReferenced()

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

Definition at line 10977 of file tablecmds.c.

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

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

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

◆ addFkRecurseReferencing()

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

Definition at line 11115 of file tablecmds.c.

11123{
11126
11130
11131 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11132 ereport(ERROR,
11134 errmsg("foreign key constraints are not supported on foreign tables")));
11135
11136 /*
11137 * Add check triggers if the constraint is ENFORCED, and if needed,
11138 * schedule them to be checked in Phase 3.
11139 *
11140 * If the relation is partitioned, drill down to do it to its partitions.
11141 */
11142 if (fkconstraint->is_enforced)
11147 indexOid,
11150
11151 if (rel->rd_rel->relkind == RELKIND_RELATION)
11152 {
11153 /*
11154 * Tell Phase 3 to check that the constraint is satisfied by existing
11155 * rows. We can skip this during table creation, when constraint is
11156 * specified as NOT ENFORCED, or when requested explicitly by
11157 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11158 * recreating a constraint following a SET DATA TYPE operation that
11159 * did not impugn its validity.
11160 */
11161 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11162 fkconstraint->is_enforced)
11163 {
11165 AlteredTableInfo *tab;
11166
11167 tab = ATGetQueueEntry(wqueue, rel);
11168
11171 newcon->contype = CONSTR_FOREIGN;
11172 newcon->refrelid = RelationGetRelid(pkrel);
11173 newcon->refindid = indexOid;
11174 newcon->conid = parentConstr;
11175 newcon->conwithperiod = fkconstraint->fk_with_period;
11176 newcon->qual = (Node *) fkconstraint;
11177
11178 tab->constraints = lappend(tab->constraints, newcon);
11179 }
11180 }
11181 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11182 {
11185
11186 /*
11187 * Triggers of the foreign keys will be manipulated a bunch of times
11188 * in the loop below. To avoid repeatedly opening/closing the trigger
11189 * catalog relation, we open it here and pass it to the subroutines
11190 * called below.
11191 */
11193
11194 /*
11195 * Recurse to take appropriate action on each partition; either we
11196 * find an existing constraint to reparent to ours, or we create a new
11197 * one.
11198 */
11199 for (int i = 0; i < pd->nparts; i++)
11200 {
11201 Relation partition = table_open(pd->oids[i], lockmode);
11202 List *partFKs;
11203 AttrMap *attmap;
11205 bool attached;
11206 ObjectAddress address;
11207
11209
11211 RelationGetDescr(rel),
11212 false);
11213 for (int j = 0; j < numfks; j++)
11214 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11215
11216 /* Check whether an existing constraint can be repurposed */
11218 attached = false;
11220 {
11222 fk,
11223 partition,
11225 numfks,
11227 pkattnum,
11231 trigrel))
11232 {
11233 attached = true;
11234 break;
11235 }
11236 }
11237 if (attached)
11238 {
11240 continue;
11241 }
11242
11243 /*
11244 * No luck finding a good constraint to reuse; create our own.
11245 */
11247 fkconstraint->conname, fkconstraint,
11248 partition, pkrel, indexOid, parentConstr,
11253 with_period);
11254
11255 /* call ourselves to finalize the creation and we're done */
11257 indexOid,
11258 address.objectId,
11259 numfks,
11260 pkattnum,
11268 lockmode,
11271 with_period);
11272
11274 }
11275
11277 }
11278}
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:528
List * RelationGetFKeyList(Relation relation)
Definition relcache.c:4722
Definition pg_list.h:54
Definition nodes.h:135
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition tablecmds.c:6622
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 6656 of file tablecmds.c.

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

12760{
12763 ScanKeyData skey[3];
12764
12765 ScanKeyInit(&skey[0],
12768 ObjectIdGetDatum(conrelid));
12769 ScanKeyInit(&skey[1],
12773 ScanKeyInit(&skey[2],
12776 CStringGetDatum(cmdcon->conname));
12777
12779 NULL, 3, skey);
12780
12782 ereport(ERROR,
12784 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12785 cmdcon->conname, get_rel_name(conrelid)));
12786
12788 recurse, recursing, lockmode);
12789
12791}
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:612
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:523
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
#define HeapTupleIsValid(tuple)
Definition htup.h:78
char * get_rel_name(Oid relid)
Definition lsyscache.c: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 13058 of file tablecmds.c.

13062{
13064 Oid conoid;
13065 ScanKeyData pkey;
13068
13070 conoid = currcon->oid;
13071
13072 ScanKeyInit(&pkey,
13075 ObjectIdGetDatum(conoid));
13076
13078 true, NULL, 1, &pkey);
13079
13081 {
13084
13085 childrel = table_open(childcon->conrelid, lockmode);
13086
13088 childtup, recurse, otherrelids, lockmode);
13090 }
13091
13093}
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 12940 of file tablecmds.c.

12943{
12947
12951 ObjectIdGetDatum(conoid));
12953 NULL, 1, &tgkey);
12955 {
12959
12960 /*
12961 * Remember OIDs of other relation(s) involved in FK constraint.
12962 * (Note: it's likely that we could skip forcing a relcache inval for
12963 * other rels that don't have a trigger whose properties change, but
12964 * let's be conservative.)
12965 */
12966 if (tgform->tgrelid != RelationGetRelid(rel))
12968 tgform->tgrelid);
12969
12970 /*
12971 * Update enable status and deferrability of RI_FKey_noaction_del,
12972 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
12973 * triggers, but not others; see createForeignKeyActionTriggers and
12974 * CreateFKCheckTrigger.
12975 */
12976 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
12977 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
12978 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
12979 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
12980 continue;
12981
12984
12985 copy_tg->tgdeferrable = deferrable;
12986 copy_tg->tginitdeferred = initdeferred;
12988
12990
12992 }
12993
12995}
HeapTuple heap_copytuple(HeapTuple tuple)
Definition heaptuple.c:686
void heap_freetuple(HeapTuple htup)
Definition heaptuple.c:1372
void CatalogTupleUpdate(Relation heapRel, const ItemPointerData *otid, HeapTuple tup)
Definition indexing.c:313
List * list_append_unique_oid(List *list, Oid datum)
Definition list.c:1380
#define InvokeObjectPostAlterHook(classId, objectId, subId)
END_CATALOG_STRUCT typedef FormData_pg_trigger * Form_pg_trigger
Definition pg_trigger.h:84

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

Referenced by ATExecAlterConstrDeferrability().

◆ AlterConstrUpdateConstraintEntry()

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

Definition at line 13100 of file tablecmds.c.

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

13017{
13019 Oid conoid;
13020 ScanKeyData pkey;
13023
13025 conoid = currcon->oid;
13026
13027 ScanKeyInit(&pkey,
13030 ObjectIdGetDatum(conoid));
13031
13033 true, NULL, 1, &pkey);
13034
13037 pkrelid, childtup, lockmode,
13042
13044}
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 19369 of file tablecmds.c.

19371{
19372 List *indexList;
19373 ListCell *l;
19374
19376
19377 foreach(l, indexList)
19378 {
19379 Oid indexOid = lfirst_oid(l);
19381
19383 thisobj.objectId = indexOid;
19384 thisobj.objectSubId = 0;
19385
19386 /*
19387 * Note: currently, the index will not have its own dependency on the
19388 * namespace, so we don't need to do changeDependencyFor(). There's no
19389 * row type in pg_type, either.
19390 *
19391 * XXX this objsMoved test may be pointless -- surely we have a single
19392 * dependency link from a relation to each index?
19393 */
19395 {
19398 false, objsMoved);
19400 }
19401 }
19402
19404}
bool object_address_present(const ObjectAddress *object, const ObjectAddresses *addrs)
void add_exact_object_address(const ObjectAddress *object, ObjectAddresses *addrs)
void list_free(List *list)
Definition list.c:1546
#define lfirst_oid(lc)
Definition pg_list.h:174
List * RelationGetIndexList(Relation relation)
Definition relcache.c:4827
void AlterRelationNamespaceInternal(Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)

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

Referenced by AlterTableNamespaceInternal().

◆ AlterRelationNamespaceInternal()

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

Definition at line 19292 of file tablecmds.c.

19296{
19300 bool already_done = false;
19301
19302 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19305 elog(ERROR, "cache lookup failed for relation %u", relOid);
19307
19308 Assert(classForm->relnamespace == oldNspOid);
19309
19310 thisobj.classId = RelationRelationId;
19311 thisobj.objectId = relOid;
19312 thisobj.objectSubId = 0;
19313
19314 /*
19315 * If the object has already been moved, don't move it again. If it's
19316 * already in the right place, don't move it, but still fire the object
19317 * access hook.
19318 */
19320 if (!already_done && oldNspOid != newNspOid)
19321 {
19322 ItemPointerData otid = classTup->t_self;
19323
19324 /* check for duplicate name (more friendly than unique-index failure) */
19325 if (get_relname_relid(NameStr(classForm->relname),
19327 ereport(ERROR,
19329 errmsg("relation \"%s\" already exists in schema \"%s\"",
19330 NameStr(classForm->relname),
19332
19333 /* classTup is a copy, so OK to scribble on */
19334 classForm->relnamespace = newNspOid;
19335
19338
19339
19340 /* Update dependency on schema if caller said so */
19341 if (hasDependEntry &&
19343 relOid,
19345 oldNspOid,
19346 newNspOid) != 1)
19347 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19348 NameStr(classForm->relname));
19349 }
19350 else
19352 if (!already_done)
19353 {
19355
19357 }
19358
19360}
#define NameStr(name)
Definition c.h:835
void UnlockTuple(Relation relation, const ItemPointerData *tid, LOCKMODE lockmode)
Definition lmgr.c:601
#define InplaceUpdateTupleLock
Definition lockdefs.h:48
char * get_namespace_name(Oid nspid)
Definition lsyscache.c: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:400

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

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

◆ AlterSeqNamespaces()

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

Definition at line 19414 of file tablecmds.c.

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

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

Referenced by AlterTableNamespaceInternal().

◆ AlterTable()

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

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

17233{
17234 List *relations = NIL;
17235 ListCell *l;
17236 ScanKeyData key[1];
17237 Relation rel;
17238 TableScanDesc scan;
17239 HeapTuple tuple;
17242 List *role_oids = roleSpecsToIds(stmt->roles);
17243
17244 /* Ensure we were not asked to move something we can't */
17245 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17246 stmt->objtype != OBJECT_MATVIEW)
17247 ereport(ERROR,
17249 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17250
17251 /* Get the orig and new tablespace OIDs */
17252 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17253 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17254
17255 /* Can't move shared relations in to or out of pg_global */
17256 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17259 ereport(ERROR,
17261 errmsg("cannot move relations in to or out of pg_global tablespace")));
17262
17263 /*
17264 * Must have CREATE rights on the new tablespace, unless it is the
17265 * database default tablespace (which all users implicitly have CREATE
17266 * rights on).
17267 */
17269 {
17271
17273 ACL_CREATE);
17274 if (aclresult != ACLCHECK_OK)
17277 }
17278
17279 /*
17280 * Now that the checks are done, check if we should set either to
17281 * InvalidOid because it is our database's default tablespace.
17282 */
17285
17288
17289 /* no-op */
17291 return new_tablespaceoid;
17292
17293 /*
17294 * Walk the list of objects in the tablespace and move them. This will
17295 * only find objects in our database, of course.
17296 */
17297 ScanKeyInit(&key[0],
17301
17303 scan = table_beginscan_catalog(rel, 1, key);
17304 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17305 {
17307 Oid relOid = relForm->oid;
17308
17309 /*
17310 * Do not move objects in pg_catalog as part of this, if an admin
17311 * really wishes to do so, they can issue the individual ALTER
17312 * commands directly.
17313 *
17314 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17315 * (TOAST will be moved with the main table).
17316 */
17317 if (IsCatalogNamespace(relForm->relnamespace) ||
17318 relForm->relisshared ||
17319 isAnyTempNamespace(relForm->relnamespace) ||
17320 IsToastNamespace(relForm->relnamespace))
17321 continue;
17322
17323 /* Only move the object type requested */
17324 if ((stmt->objtype == OBJECT_TABLE &&
17325 relForm->relkind != RELKIND_RELATION &&
17326 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17327 (stmt->objtype == OBJECT_INDEX &&
17328 relForm->relkind != RELKIND_INDEX &&
17329 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17330 (stmt->objtype == OBJECT_MATVIEW &&
17331 relForm->relkind != RELKIND_MATVIEW))
17332 continue;
17333
17334 /* Check if we are only moving objects owned by certain roles */
17335 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17336 continue;
17337
17338 /*
17339 * Handle permissions-checking here since we are locking the tables
17340 * and also to avoid doing a bunch of work only to fail part-way. Note
17341 * that permissions will also be checked by AlterTableInternal().
17342 *
17343 * Caller must be considered an owner on the table to move it.
17344 */
17347 NameStr(relForm->relname));
17348
17349 if (stmt->nowait &&
17351 ereport(ERROR,
17353 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17354 get_namespace_name(relForm->relnamespace),
17355 NameStr(relForm->relname))));
17356 else
17358
17359 /* Add to our list of objects to move */
17360 relations = lappend_oid(relations, relOid);
17361 }
17362
17363 table_endscan(scan);
17365
17366 if (relations == NIL)
17369 errmsg("no matching relations in tablespace \"%s\" found",
17370 orig_tablespaceoid == InvalidOid ? "(database default)" :
17372
17373 /* Everything is locked, loop through and move all of the relations. */
17374 foreach(l, relations)
17375 {
17376 List *cmds = NIL;
17378
17380 cmd->name = stmt->new_tablespacename;
17381
17382 cmds = lappend(cmds, cmd);
17383
17385 /* OID is set by AlterTableInternal */
17386 AlterTableInternal(lfirst_oid(l), cmds, false);
17388 }
17389
17390 return new_tablespaceoid;
17391}
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
@ ACLCHECK_NOT_OWNER
Definition acl.h:186
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2672
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3879
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4133
char * get_tablespace_name(Oid spc_oid)
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
bool IsToastNamespace(Oid namespaceId)
Definition catalog.c:261
bool IsCatalogNamespace(Oid namespaceId)
Definition catalog.c:243
#define NOTICE
Definition elog.h:36
void EventTriggerAlterTableStart(const Node *parsetree)
void EventTriggerAlterTableEnd(void)
Oid MyDatabaseTableSpace
Definition globals.c:98
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition heapam.c:1435
List * lappend_oid(List *list, Oid datum)
Definition list.c:375
bool list_member_oid(const List *list, Oid datum)
Definition list.c:722
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:151
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:107
char get_rel_relkind(Oid relid)
Definition lsyscache.c: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:1061
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 19184 of file tablecmds.c.

19185{
19186 Relation rel;
19187 Oid relid;
19188 Oid oldNspOid;
19189 Oid nspOid;
19190 RangeVar *newrv;
19193
19195 stmt->missing_ok ? RVR_MISSING_OK : 0,
19197 stmt);
19198
19199 if (!OidIsValid(relid))
19200 {
19202 (errmsg("relation \"%s\" does not exist, skipping",
19203 stmt->relation->relname)));
19204 return InvalidObjectAddress;
19205 }
19206
19207 rel = relation_open(relid, NoLock);
19208
19210
19211 /* If it's an owned sequence, disallow moving it by itself. */
19212 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19213 {
19214 Oid tableId;
19215 int32 colId;
19216
19217 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19219 ereport(ERROR,
19221 errmsg("cannot move an owned sequence into another schema"),
19222 errdetail("Sequence \"%s\" is linked to table \"%s\".",
19225 }
19226
19227 /* Get and lock schema OID and check its permissions. */
19228 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19230
19231 /* common checks on switching namespaces */
19233
19237
19239
19240 if (oldschema)
19242
19243 /* close rel, but keep lock until commit */
19244 relation_close(rel, NoLock);
19245
19246 return myself;
19247}
int32_t int32
Definition c.h:620
ObjectAddresses * new_object_addresses(void)
void free_object_addresses(ObjectAddresses *addrs)
int errdetail(const char *fmt,...) pg_attribute_printf(1
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition makefuncs.c:473
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition namespace.c:740
void CheckSetNamespace(Oid oldNspOid, Oid nspOid)
Definition namespace.c:3531
const ObjectAddress InvalidObjectAddress
bool sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
Definition pg_depend.c:901
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)

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

Referenced by ExecAlterObjectSchemaStmt().

◆ AlterTableNamespaceInternal()

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

Definition at line 19255 of file tablecmds.c.

19257{
19259
19260 Assert(objsMoved != NULL);
19261
19262 /* OK, modify the pg_class row and pg_depend entry */
19264
19266 nspOid, true, objsMoved);
19267
19268 /* Fix the table's row type too, if it has one */
19269 if (OidIsValid(rel->rd_rel->reltype))
19271 false, /* isImplicitArray */
19272 false, /* ignoreDependent */
19273 false, /* errorOnTableType */
19274 objsMoved);
19275
19276 /* Fix other dependent stuff */
19281 false, objsMoved);
19282
19284}
void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, Oid newNspId, bool isType, ObjectAddresses *objsMoved)
static void AlterSeqNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
static void AlterIndexNamespaces(Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
Oid AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, bool isImplicitArray, bool ignoreDependent, bool errorOnTableType, ObjectAddresses *objsMoved)
Definition typecmds.c:4199

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

Referenced by AlterObjectNamespace_oid(), and AlterTableNamespace().

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

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

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

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

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

Referenced by ATExecAddConstraint().

◆ ATCheckPartitionsNotInUse()

static void ATCheckPartitionsNotInUse ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 6925 of file tablecmds.c.

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

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

Referenced by ATPrepCmd().

◆ ATColumnChangeRequiresRewrite()

static bool ATColumnChangeRequiresRewrite ( Node expr,
AttrNumber  varattno 
)
static

Definition at line 14925 of file tablecmds.c.

14926{
14927 Assert(expr != NULL);
14928
14929 for (;;)
14930 {
14931 /* only one varno, so no need to check that */
14932 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
14933 return false;
14934 else if (IsA(expr, RelabelType))
14935 expr = (Node *) ((RelabelType *) expr)->arg;
14936 else if (IsA(expr, CoerceToDomain))
14937 {
14938 CoerceToDomain *d = (CoerceToDomain *) expr;
14939
14941 return true;
14942 expr = (Node *) d->arg;
14943 }
14944 else if (IsA(expr, FuncExpr))
14945 {
14946 FuncExpr *f = (FuncExpr *) expr;
14947
14948 switch (f->funcid)
14949 {
14953 return true;
14954 else
14955 expr = linitial(f->args);
14956 break;
14957 default:
14958 return true;
14959 }
14960 }
14961 else
14962 return true;
14963 }
14964}
bool TimestampTimestampTzRequiresRewrite(void)
Definition timestamp.c:6411
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 22248 of file tablecmds.c.

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

19699{
19701
19702 foreach(cur_item, on_commits)
19703 {
19705
19706 if (!isCommit && oc->creating_subid == mySubid)
19707 {
19708 /* cur_item must be removed */
19710 pfree(oc);
19711 }
19712 else
19713 {
19714 /* cur_item must be preserved */
19715 if (oc->creating_subid == mySubid)
19716 oc->creating_subid = parentSubid;
19717 if (oc->deleting_subid == mySubid)
19718 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19719 }
19720 }
19721}
#define InvalidSubTransactionId
Definition c.h:742
#define foreach_delete_current(lst, var_or_cell)
Definition pg_list.h:423
static List * on_commits
Definition tablecmds.c: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 19665 of file tablecmds.c.

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

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

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

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

Referenced by ATExecAddColumn(), and ATExecCmd().

◆ ATExecAddConstraint()

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

Definition at line 9873 of file tablecmds.c.

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

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

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

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

References Assert, DefineIndex(), fb(), index_close(), index_open(), InvalidOid, IsA, NoLock, ObjectAddress::objectId, RelationData::rd_createSubid, RelationData::rd_firstRelfilelocatorSubid, RelationData::rd_locator, RelationGetRelid, RelationPreserveStorage(), RelFileNumberIsValid, AlteredTableInfo::rewrite, and stmt.

Referenced by ATExecCmd().

◆ ATExecAddIndexConstraint()

static ObjectAddress ATExecAddIndexConstraint ( AlteredTableInfo tab,
Relation  rel,
IndexStmt stmt,
LOCKMODE  lockmode 
)
static

Definition at line 9781 of file tablecmds.c.

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

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

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

Referenced by ATExecCmd().

◆ ATExecAddOf()

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

Definition at line 18454 of file tablecmds.c.

18455{
18456 Oid relid = RelationGetRelid(rel);
18459 Oid typeid;
18462 SysScanDesc scan;
18465 type_attno;
18469 typeobj;
18471
18472 /* Validate the type. */
18473 typetuple = typenameType(NULL, ofTypename, NULL);
18476 typeid = typeform->oid;
18477
18478 /* Fail if the table has any inheritance parents. */
18480 ScanKeyInit(&key,
18483 ObjectIdGetDatum(relid));
18485 true, NULL, 1, &key);
18487 ereport(ERROR,
18489 errmsg("typed tables cannot inherit")));
18490 systable_endscan(scan);
18492
18493 /*
18494 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18495 * require that the order also match. However, attnotnull need not match.
18496 */
18499 table_attno = 1;
18500 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18501 {
18503 table_attr;
18504 const char *type_attname,
18506
18507 /* Get the next non-dropped type attribute. */
18509 if (type_attr->attisdropped)
18510 continue;
18511 type_attname = NameStr(type_attr->attname);
18512
18513 /* Get the next non-dropped table attribute. */
18514 do
18515 {
18516 if (table_attno > tableTupleDesc->natts)
18517 ereport(ERROR,
18519 errmsg("table is missing column \"%s\"",
18520 type_attname)));
18522 table_attno++;
18523 } while (table_attr->attisdropped);
18524 table_attname = NameStr(table_attr->attname);
18525
18526 /* Compare name. */
18528 ereport(ERROR,
18530 errmsg("table has column \"%s\" where type requires \"%s\"",
18532
18533 /* Compare type. */
18534 if (table_attr->atttypid != type_attr->atttypid ||
18535 table_attr->atttypmod != type_attr->atttypmod ||
18536 table_attr->attcollation != type_attr->attcollation)
18537 ereport(ERROR,
18539 errmsg("table \"%s\" has different type for column \"%s\"",
18541 }
18543
18544 /* Any remaining columns at the end of the table had better be dropped. */
18545 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18546 {
18548 table_attno - 1);
18549
18550 if (!table_attr->attisdropped)
18551 ereport(ERROR,
18553 errmsg("table has extra column \"%s\"",
18554 NameStr(table_attr->attname))));
18555 }
18556
18557 /* If the table was already typed, drop the existing dependency. */
18558 if (rel->rd_rel->reloftype)
18559 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18561
18562 /* Record a dependency on the new type. */
18563 tableobj.classId = RelationRelationId;
18564 tableobj.objectId = relid;
18565 tableobj.objectSubId = 0;
18566 typeobj.classId = TypeRelationId;
18567 typeobj.objectId = typeid;
18568 typeobj.objectSubId = 0;
18570
18571 /* Update pg_class.reloftype */
18575 elog(ERROR, "cache lookup failed for relation %u", relid);
18576 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18578
18580
18583
18585
18586 return typeobj;
18587}
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:7207
#define ReleaseTupleDesc(tupdesc)
Definition tupdesc.h:240
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition typcache.c:1947

References AccessShareLock, BTEqualStrategyNumber, CatalogTupleUpdate(), check_of_type(), DEPENDENCY_NORMAL, drop_parent_dependency(), elog, ereport, errcode(), errmsg, ERROR, fb(), Form_pg_type, GETSTRUCT(), heap_freetuple(), HeapTupleIsValid, InvokeObjectPostAlterHook, lookup_rowtype_tupdesc(), NAMEDATALEN, NameStr, ObjectIdGetDatum(), RelationData::rd_rel, recordDependencyOn(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), ReleaseTupleDesc, RowExclusiveLock, ScanKeyInit(), SearchSysCacheCopy1, systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), TupleDescAttr(), and typenameType().

Referenced by ATExecCmd().

◆ ATExecAddStatistics()

static ObjectAddress ATExecAddStatistics ( AlteredTableInfo tab,
Relation  rel,
CreateStatsStmt stmt,
bool  is_rebuild,
LOCKMODE  lockmode 
)
static

Definition at line 9760 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecAlterCheckConstrEnforceability()

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

Definition at line 12640 of file tablecmds.c.

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

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

Referenced by AlterCheckConstrEnforceabilityRecurse(), and ATExecAlterConstraintInternal().

◆ ATExecAlterColumnGenericOptions()

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

Definition at line 16201 of file tablecmds.c.

16205{
16208 ForeignServer *server;
16210 HeapTuple tuple;
16211 HeapTuple newtuple;
16212 bool isnull;
16216 Datum datum;
16220 ObjectAddress address;
16221
16222 if (options == NIL)
16223 return InvalidObjectAddress;
16224
16225 /* First, determine FDW validator associated to the foreign table. */
16228 if (!HeapTupleIsValid(tuple))
16229 ereport(ERROR,
16231 errmsg("foreign table \"%s\" does not exist",
16234 server = GetForeignServer(fttableform->ftserver);
16235 fdw = GetForeignDataWrapper(server->fdwid);
16236
16238 ReleaseSysCache(tuple);
16239
16242 if (!HeapTupleIsValid(tuple))
16243 ereport(ERROR,
16245 errmsg("column \"%s\" of relation \"%s\" does not exist",
16247
16248 /* Prevent them from altering a system attribute */
16250 attnum = atttableform->attnum;
16251 if (attnum <= 0)
16252 ereport(ERROR,
16254 errmsg("cannot alter system column \"%s\"", colName)));
16255
16256
16257 /* Initialize buffers for new tuple values */
16258 memset(repl_val, 0, sizeof(repl_val));
16259 memset(repl_null, false, sizeof(repl_null));
16260 memset(repl_repl, false, sizeof(repl_repl));
16261
16262 /* Extract the current options */
16263 datum = SysCacheGetAttr(ATTNAME,
16264 tuple,
16266 &isnull);
16267 if (isnull)
16268 datum = PointerGetDatum(NULL);
16269
16270 /* Transform the options */
16272 datum,
16273 options,
16274 fdw->fdwvalidator);
16275
16276 if (DatumGetPointer(datum) != NULL)
16278 else
16280
16282
16283 /* Everything looks good - update the tuple */
16284
16285 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16287
16288 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16289
16291 RelationGetRelid(rel),
16292 atttableform->attnum);
16294 RelationGetRelid(rel), attnum);
16295
16296 ReleaseSysCache(tuple);
16297
16299
16300 heap_freetuple(newtuple);
16301
16302 return address;
16303}
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition foreign.c:39
ForeignServer * GetForeignServer(Oid serverid)
Definition foreign.c:114
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition heaptuple.c:1118
END_CATALOG_STRUCT typedef FormData_pg_foreign_table * Form_pg_foreign_table
static 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:476
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:596

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

Referenced by ATExecCmd().

◆ ATExecAlterColumnType()

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

Definition at line 14972 of file tablecmds.c.

14974{
14975 char *colName = cmd->name;
14976 ColumnDef *def = (ColumnDef *) cmd->def;
14977 TypeName *typeName = def->typeName;
14980 attOldTup;
14984 Oid targettype;
14985 int32 targettypmod;
14990 ScanKeyData key[3];
14991 SysScanDesc scan;
14993 ObjectAddress address;
14994
14995 /*
14996 * Clear all the missing values if we're rewriting the table, since this
14997 * renders them pointless.
14998 */
14999 if (tab->rewrite)
15000 {
15002
15006 /* make sure we don't conflict with later attribute modifications */
15008 }
15009
15011
15012 /* Look up the target column */
15014 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
15015 ereport(ERROR,
15017 errmsg("column \"%s\" of relation \"%s\" does not exist",
15020 attnum = attTup->attnum;
15022
15023 /* Check for multiple ALTER TYPE on same column --- can't cope */
15024 if (attTup->atttypid != attOldTup->atttypid ||
15025 attTup->atttypmod != attOldTup->atttypmod)
15026 ereport(ERROR,
15028 errmsg("cannot alter type of column \"%s\" twice",
15029 colName)));
15030
15031 /* Look up the target type (should not fail, since prep found it) */
15032 typeTuple = typenameType(NULL, typeName, &targettypmod);
15034 targettype = tform->oid;
15035 /* And the collation */
15036 targetcollid = GetColumnDefCollation(NULL, def, targettype);
15037
15038 /*
15039 * If there is a default expression for the column, get it and ensure we
15040 * can coerce it to the new datatype. (We must do this before changing
15041 * the column type, because build_column_default itself will try to
15042 * coerce, and will not issue the error message we want if it fails.)
15043 *
15044 * We remove any implicit coercion steps at the top level of the old
15045 * default expression; this has been agreed to satisfy the principle of
15046 * least surprise. (The conversion to the new column type should act like
15047 * it started from what the user sees as the stored expression, and the
15048 * implicit coercions aren't going to be shown.)
15049 */
15050 if (attTup->atthasdef)
15051 {
15055 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
15057 targettype, targettypmod,
15060 -1);
15061 if (defaultexpr == NULL)
15062 {
15063 if (attTup->attgenerated)
15064 ereport(ERROR,
15066 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
15067 colName, format_type_be(targettype))));
15068 else
15069 ereport(ERROR,
15071 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
15072 colName, format_type_be(targettype))));
15073 }
15074 }
15075 else
15076 defaultexpr = NULL;
15077
15078 /*
15079 * Find everything that depends on the column (constraints, indexes, etc),
15080 * and record enough information to let us recreate the objects.
15081 *
15082 * The actual recreation does not happen here, but only after we have
15083 * performed all the individual ALTER TYPE operations. We have to save
15084 * the info before executing ALTER TYPE, though, else the deparser will
15085 * get confused.
15086 */
15088
15089 /*
15090 * Now scan for dependencies of this column on other things. The only
15091 * things we should find are the dependency on the column datatype and
15092 * possibly a collation dependency. Those can be removed.
15093 */
15095
15096 ScanKeyInit(&key[0],
15100 ScanKeyInit(&key[1],
15104 ScanKeyInit(&key[2],
15108
15110 NULL, 3, key);
15111
15113 {
15116
15117 foundObject.classId = foundDep->refclassid;
15118 foundObject.objectId = foundDep->refobjid;
15119 foundObject.objectSubId = foundDep->refobjsubid;
15120
15121 if (foundDep->deptype != DEPENDENCY_NORMAL)
15122 elog(ERROR, "found unexpected dependency type '%c'",
15123 foundDep->deptype);
15124 if (!(foundDep->refclassid == TypeRelationId &&
15125 foundDep->refobjid == attTup->atttypid) &&
15126 !(foundDep->refclassid == CollationRelationId &&
15127 foundDep->refobjid == attTup->attcollation))
15128 elog(ERROR, "found unexpected dependency for column: %s",
15130
15131 CatalogTupleDelete(depRel, &depTup->t_self);
15132 }
15133
15134 systable_endscan(scan);
15135
15137
15138 /*
15139 * Here we go --- change the recorded column type and collation. (Note
15140 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
15141 * fix up the missing value if any.
15142 */
15143 if (attTup->atthasmissing)
15144 {
15146 bool missingNull;
15147
15148 /* if rewrite is true the missing value should already be cleared */
15149 Assert(tab->rewrite == 0);
15150
15151 /* Get the missing value datum */
15154 attrelation->rd_att,
15155 &missingNull);
15156
15157 /* if it's a null array there is nothing to do */
15158
15159 if (!missingNull)
15160 {
15161 /*
15162 * Get the datum out of the array and repack it in a new array
15163 * built with the new type data. We assume that since the table
15164 * doesn't need rewriting, the actual Datum doesn't need to be
15165 * changed, only the array metadata.
15166 */
15167
15168 int one = 1;
15169 bool isNull;
15171 bool nullsAtt[Natts_pg_attribute] = {0};
15172 bool replacesAtt[Natts_pg_attribute] = {0};
15174
15176 1,
15177 &one,
15178 0,
15179 attTup->attlen,
15180 attTup->attbyval,
15181 attTup->attalign,
15182 &isNull);
15184 1,
15185 targettype,
15186 tform->typlen,
15187 tform->typbyval,
15188 tform->typalign));
15189
15193
15197 heapTup = newTup;
15199 }
15200 }
15201
15202 attTup->atttypid = targettype;
15203 attTup->atttypmod = targettypmod;
15204 attTup->attcollation = targetcollid;
15205 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
15206 ereport(ERROR,
15208 errmsg("too many array dimensions"));
15209 attTup->attndims = list_length(typeName->arrayBounds);
15210 attTup->attlen = tform->typlen;
15211 attTup->attbyval = tform->typbyval;
15212 attTup->attalign = tform->typalign;
15213 attTup->attstorage = tform->typstorage;
15214 attTup->attcompression = InvalidCompressionMethod;
15215
15217
15219
15221
15222 /* Install dependencies on new datatype and collation */
15225
15226 /*
15227 * Drop any pg_statistic entry for the column, since it's now wrong type
15228 */
15230
15232 RelationGetRelid(rel), attnum);
15233
15234 /*
15235 * Update the default, if present, by brute force --- remove and re-add
15236 * the default. Probably unsafe to take shortcuts, since the new version
15237 * may well have additional dependencies. (It's okay to do this now,
15238 * rather than after other ALTER TYPE commands, since the default won't
15239 * depend on other column types.)
15240 */
15241 if (defaultexpr)
15242 {
15243 /*
15244 * If it's a GENERATED default, drop its dependency records, in
15245 * particular its INTERNAL dependency on the column, which would
15246 * otherwise cause dependency.c to refuse to perform the deletion.
15247 */
15248 if (attTup->attgenerated)
15249 {
15251
15252 if (!OidIsValid(attrdefoid))
15253 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15254 RelationGetRelid(rel), attnum);
15256 }
15257
15258 /*
15259 * Make updates-so-far visible, particularly the new pg_attribute row
15260 * which will be updated again.
15261 */
15263
15264 /*
15265 * We use RESTRICT here for safety, but at present we do not expect
15266 * anything to depend on the default.
15267 */
15269 true);
15270
15271 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15272 }
15273
15275 RelationGetRelid(rel), attnum);
15276
15277 /* Cleanup */
15279
15280 return address;
15281}
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Datum array_get_element(Datum arraydatum, int nSubscripts, int *indx, int arraytyplen, int elmlen, bool elmbyval, char elmalign, bool *isNull)
#define PG_INT16_MAX
Definition c.h:670
void RelationClearMissing(Relation rel)
Definition heap.c: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:294
static void RememberAllDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel, AttrNumber attnum, const char *colName)
#define InvalidCompressionMethod

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

Referenced by ATExecCmd().

◆ ATExecAlterConstraint()

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

Definition at line 12275 of file tablecmds.c.

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

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

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

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

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

12506{
12508 Oid conoid;
12509 Relation rel;
12510 bool changed = false;
12511
12512 /* Since this function recurses, it could be driven to stack overflow */
12514
12515 Assert(cmdcon->alterEnforceability);
12516
12518 conoid = currcon->oid;
12519
12520 /* Should be foreign key constraint */
12521 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12522
12523 rel = table_open(currcon->conrelid, lockmode);
12524
12525 if (currcon->conenforced != cmdcon->is_enforced)
12526 {
12528 changed = true;
12529 }
12530
12531 /* Drop triggers */
12532 if (!cmdcon->is_enforced)
12533 {
12534 /*
12535 * When setting a constraint to NOT ENFORCED, the constraint triggers
12536 * need to be dropped. Therefore, we must process the child relations
12537 * first, followed by the parent, to account for dependencies.
12538 */
12539 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12543 lockmode, InvalidOid, InvalidOid,
12545
12546 /* Drop all the triggers */
12548 }
12549 else if (changed) /* Create triggers */
12550 {
12555
12556 /* Prepare the minimal information required for trigger creation. */
12558
12559 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12560 fkconstraint->fk_matchtype = currcon->confmatchtype;
12561 fkconstraint->fk_upd_action = currcon->confupdtype;
12562 fkconstraint->fk_del_action = currcon->confdeltype;
12563 fkconstraint->deferrable = currcon->condeferrable;
12564 fkconstraint->initdeferred = currcon->condeferred;
12565
12566 /* Create referenced triggers */
12567 if (currcon->conrelid == fkrelid)
12569 currcon->confrelid,
12571 conoid,
12572 currcon->conindid,
12577
12578 /* Create referencing triggers */
12579 if (currcon->confrelid == pkrelid)
12581 pkrelid,
12583 conoid,
12584 currcon->conindid,
12589
12590 /*
12591 * Tell Phase 3 to check that the constraint is satisfied by existing
12592 * rows. Only applies to leaf partitions, and (for constraints that
12593 * reference a partitioned table) only if this is not one of the
12594 * pg_constraint rows that exist solely to support action triggers.
12595 */
12596 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12597 currcon->confrelid == pkrelid)
12598 {
12599 AlteredTableInfo *tab;
12601
12603 newcon->name = fkconstraint->conname;
12604 newcon->contype = CONSTR_FOREIGN;
12605 newcon->refrelid = currcon->confrelid;
12606 newcon->refindid = currcon->conindid;
12607 newcon->conid = currcon->oid;
12608 newcon->qual = (Node *) fkconstraint;
12609
12610 /* Find or create work queue entry for this table */
12611 tab = ATGetQueueEntry(wqueue, rel);
12612 tab->constraints = lappend(tab->constraints, newcon);
12613 }
12614
12615 /*
12616 * If the table at either end of the constraint is partitioned, we
12617 * need to recurse and create triggers for each constraint that is a
12618 * child of this one.
12619 */
12620 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12624 lockmode,
12629 }
12630
12631 table_close(rel, NoLock);
12632
12633 return changed;
12634}
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 20527 of file tablecmds.c.

20529{
20531 catalog;
20534 SysScanDesc scan;
20536 AttrNumber attno;
20537 int natts;
20539 ObjectAddress address;
20540 const char *trigger_name;
20544 ParseState *pstate = make_parsestate(NULL);
20545
20546 pstate->p_sourcetext = context->queryString;
20547
20548 /*
20549 * We must lock the default partition if one exists, because attaching a
20550 * new partition will change its partition constraint.
20551 */
20556
20558
20559 /*
20560 * XXX I think it'd be a good idea to grab locks on all tables referenced
20561 * by FKs at this point also.
20562 */
20563
20564 /*
20565 * Must be owner of both parent and source table -- parent was checked by
20566 * ATSimplePermissions call in ATPrepCmd
20567 */
20570
20571 /* A partition can only have one parent */
20572 if (attachrel->rd_rel->relispartition)
20573 ereport(ERROR,
20575 errmsg("\"%s\" is already a partition",
20577
20578 if (OidIsValid(attachrel->rd_rel->reloftype))
20579 ereport(ERROR,
20581 errmsg("cannot attach a typed table as partition")));
20582
20583 /*
20584 * Disallow attaching a partition if the table is referenced in a
20585 * publication EXCEPT clause. Changing the partition hierarchy could alter
20586 * the effective publication membership.
20587 */
20589 if (exceptpuboids != NIL)
20590 {
20591 bool first = true;
20593
20595
20597 {
20598 char *pubname = get_publication_name(pubid, false);
20599
20600 if (!first)
20601 {
20602 /*
20603 * translator: This is a separator in a list of publication
20604 * names.
20605 */
20607 }
20608
20609 first = false;
20610
20611 appendStringInfo(&pubnames, _("\"%s\""), pubname);
20612 }
20613
20614 ereport(ERROR,
20616 errmsg_plural("cannot attach table \"%s\" as partition because it is referenced in publication %s EXCEPT clause",
20617 "cannot attach table \"%s\" as partition because it is referenced in publications %s EXCEPT clause",
20620 pubnames.data),
20621 errdetail("The publication EXCEPT clause cannot contain tables that are partitions."),
20622 errhint("Change the publication's EXCEPT clause using ALTER PUBLICATION ... SET ALL TABLES."));
20623 }
20624
20626
20627 /*
20628 * Table being attached should not already be part of inheritance; either
20629 * as a child table...
20630 */
20637 NULL, 1, &skey);
20639 ereport(ERROR,
20641 errmsg("cannot attach inheritance child as partition")));
20642 systable_endscan(scan);
20643
20644 /* ...or as a parent table (except the case when it is partitioned) */
20650 1, &skey);
20652 attachrel->rd_rel->relkind == RELKIND_RELATION)
20653 ereport(ERROR,
20655 errmsg("cannot attach inheritance parent as partition")));
20656 systable_endscan(scan);
20658
20659 /*
20660 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20661 * particular, this disallows making a rel a partition of itself.)
20662 *
20663 * We do that by checking if rel is a member of the list of attachrel's
20664 * partitions provided the latter is partitioned at all. We want to avoid
20665 * having to construct this list again, so we request the strongest lock
20666 * on all partitions. We need the strongest lock, because we may decide
20667 * to scan them if we find out that the table being attached (or its leaf
20668 * partitions) may contain rows that violate the partition constraint. If
20669 * the table has a constraint that would prevent such rows, which by
20670 * definition is present in all the partitions, we need not scan the
20671 * table, nor its partitions. But we cannot risk a deadlock by taking a
20672 * weaker lock now and the stronger one only when needed.
20673 */
20677 ereport(ERROR,
20679 errmsg("circular inheritance not allowed"),
20680 errdetail("\"%s\" is already a child of \"%s\".",
20683
20684 /* If the parent is permanent, so must be all of its partitions. */
20685 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20686 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20687 ereport(ERROR,
20689 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20691
20692 /* Temp parent cannot have a partition that is itself not a temp */
20693 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20694 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20695 ereport(ERROR,
20697 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20699
20700 /* If the parent is temp, it must belong to this session */
20701 if (RELATION_IS_OTHER_TEMP(rel))
20702 ereport(ERROR,
20704 errmsg("cannot attach as partition of temporary relation of another session")));
20705
20706 /* Ditto for the partition */
20708 ereport(ERROR,
20710 errmsg("cannot attach temporary relation of another session as partition")));
20711
20712 /*
20713 * Check if attachrel has any identity columns or any columns that aren't
20714 * in the parent.
20715 */
20717 natts = tupleDesc->natts;
20718 for (attno = 1; attno <= natts; attno++)
20719 {
20721 char *attributeName = NameStr(attribute->attname);
20722
20723 /* Ignore dropped */
20724 if (attribute->attisdropped)
20725 continue;
20726
20727 if (attribute->attidentity)
20728 ereport(ERROR,
20730 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20732 errdetail("The new partition may not contain an identity column."));
20733
20734 /* Try to find the column in parent (matching on column name) */
20738 ereport(ERROR,
20740 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20743 errdetail("The new partition may contain only the columns present in parent.")));
20744 }
20745
20746 /*
20747 * If child_rel has row-level triggers with transition tables, we
20748 * currently don't allow it to become a partition. See also prohibitions
20749 * in ATExecAddInherit() and CreateTrigger().
20750 */
20752 if (trigger_name != NULL)
20753 ereport(ERROR,
20755 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20757 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20758
20759 /*
20760 * Check that the new partition's bound is valid and does not overlap any
20761 * of existing partitions of the parent - note that it does not return on
20762 * error.
20763 */
20765 cmd->bound, pstate);
20766
20768
20769 /*
20770 * Generate a partition constraint from the partition bound specification.
20771 * If the parent itself is a partition, make sure to include its
20772 * constraint as well.
20773 */
20775
20776 /*
20777 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20778 * since it's needed later to construct the constraint expression for
20779 * validating against the default partition, if any.
20780 */
20783
20784 /* Skip validation if there are no constraints to validate. */
20785 if (partConstraint)
20786 {
20787 /*
20788 * Run the partition quals through const-simplification similar to
20789 * check constraints. We skip canonicalize_qual, though, because
20790 * partition quals should be in canonical form already.
20791 */
20794 (Node *) partConstraint);
20795
20796 /* XXX this sure looks wrong */
20798
20799 /*
20800 * Adjust the generated constraint to match this partition's attribute
20801 * numbers.
20802 */
20804 rel);
20805
20806 /* Validate partition constraints against the table being attached. */
20808 false);
20809 }
20810
20811 /*
20812 * If we're attaching a partition other than the default partition and a
20813 * default one exists, then that partition's partition constraint changes,
20814 * so add an entry to the work queue to validate it, too. (We must not do
20815 * this when the partition being attached is the default one; we already
20816 * did it above!)
20817 */
20819 {
20822
20823 Assert(!cmd->bound->is_default);
20824
20825 /* we already hold a lock on the default partition */
20829
20830 /*
20831 * Map the Vars in the constraint expression from rel's attnos to
20832 * defaultrel's.
20833 */
20836 1, defaultrel, rel);
20838 defPartConstraint, true);
20839
20840 /* keep our lock until commit. */
20842 }
20843
20845
20846 /*
20847 * If the partition we just attached is partitioned itself, invalidate
20848 * relcache for all descendent partitions too to ensure that their
20849 * rd_partcheck expression trees are rebuilt; partitions already locked at
20850 * the beginning of this function.
20851 */
20852 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20853 {
20854 ListCell *l;
20855
20856 foreach(l, attachrel_children)
20857 {
20859 }
20860 }
20861
20862 /* keep our lock until commit */
20864
20865 return address;
20866}
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:3892
Expr * make_ands_explicit(List *andclauses)
Definition makefuncs.c:799
ParseState * make_parsestate(ParseState *parentParseState)
Definition parse_node.c:39
void check_new_partition_bound(char *relname, Relation parent, PartitionBoundSpec *spec, ParseState *pstate)
List * get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
Definition partbounds.c:250
List * RelationGetPartitionQual(Relation rel)
Definition partcache.c:277
Oid get_default_oid_from_partdesc(PartitionDesc partdesc)
Definition partdesc.c:501
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition partition.c:222
List * get_proposed_default_constraint(List *new_part_constraints)
Definition partition.c:370
List * GetRelationExcludedPublications(Oid relid)
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void appendStringInfoString(StringInfo str, const char *s)
Definition stringinfo.c:230
void initStringInfo(StringInfo str)
Definition stringinfo.c:97
const char * queryString
Definition utility.h:33
const char * p_sourcetext
Definition parse_node.h:214
PartitionBoundSpec * bound
RangeVar * name
#define SearchSysCacheExists2(cacheId, key1, key2)
Definition syscache.h:102
static void attachPartitionTable(List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
static void QueuePartitionConstraintValidation(List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)

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

Referenced by ATExecCmd().

◆ ATExecAttachPartitionIdx()

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

Definition at line 21885 of file tablecmds.c.

21886{
21889 Relation parentTbl;
21890 ObjectAddress address;
21891 Oid partIdxId;
21894
21895 /*
21896 * We need to obtain lock on the index 'name' to modify it, but we also
21897 * need to read its owning table's tuple descriptor -- so we need to lock
21898 * both. To avoid deadlocks, obtain lock on the table before doing so on
21899 * the index. Furthermore, we need to examine the parent table of the
21900 * partition, so lock that one too.
21901 */
21902 state.partitionOid = InvalidOid;
21903 state.parentTblOid = parentIdx->rd_index->indrelid;
21904 state.lockedParentTbl = false;
21905 partIdxId =
21908 &state);
21909 /* Not there? */
21910 if (!OidIsValid(partIdxId))
21911 ereport(ERROR,
21913 errmsg("index \"%s\" does not exist", name->relname)));
21914
21915 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
21917
21918 /* we already hold locks on both tables, so this is safe: */
21919 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
21920 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
21921
21923
21924 /* Silently do nothing if already in the right state */
21925 currParent = partIdx->rd_rel->relispartition ?
21927 if (currParent != RelationGetRelid(parentIdx))
21928 {
21931 AttrMap *attmap;
21932 bool found;
21933 int i;
21937
21938 /*
21939 * If this partition already has an index attached, refuse the
21940 * operation.
21941 */
21943
21945 ereport(ERROR,
21947 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21949 RelationGetRelationName(parentIdx)),
21950 errdetail("Index \"%s\" is already attached to another index.",
21952
21953 /* Make sure it indexes a partition of the other index's table */
21954 partDesc = RelationGetPartitionDesc(parentTbl, true);
21955 found = false;
21956 for (i = 0; i < partDesc->nparts; i++)
21957 {
21958 if (partDesc->oids[i] == state.partitionOid)
21959 {
21960 found = true;
21961 break;
21962 }
21963 }
21964 if (!found)
21965 ereport(ERROR,
21967 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21969 RelationGetRelationName(parentIdx)),
21970 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
21972 RelationGetRelationName(parentTbl))));
21973
21974 /* Ensure the indexes are compatible */
21976 parentInfo = BuildIndexInfo(parentIdx);
21978 RelationGetDescr(parentTbl),
21979 false);
21981 partIdx->rd_indcollation,
21982 parentIdx->rd_indcollation,
21983 partIdx->rd_opfamily,
21984 parentIdx->rd_opfamily,
21985 attmap))
21986 ereport(ERROR,
21988 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
21990 RelationGetRelationName(parentIdx)),
21991 errdetail("The index definitions do not match.")));
21992
21993 /*
21994 * If there is a constraint in the parent, make sure there is one in
21995 * the child too.
21996 */
21998 RelationGetRelid(parentIdx));
21999
22001 {
22003 partIdxId);
22004 if (!OidIsValid(cldConstrId))
22005 ereport(ERROR,
22007 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22009 RelationGetRelationName(parentIdx)),
22010 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
22011 RelationGetRelationName(parentIdx),
22012 RelationGetRelationName(parentTbl),
22014 }
22015
22016 /*
22017 * If it's a primary key, make sure the columns in the partition are
22018 * NOT NULL.
22019 */
22020 if (parentIdx->rd_index->indisprimary)
22022
22023 /* All good -- do it */
22028
22030
22031 validatePartitionedIndex(parentIdx, parentTbl);
22032 }
22033
22034 relation_close(parentTbl, AccessShareLock);
22035 /* keep these locks till commit */
22038
22039 return address;
22040}
bool CompareIndexInfo(const IndexInfo *info1, const IndexInfo *info2, const Oid *collations1, const Oid *collations2, const Oid *opfamilies1, const Oid *opfamilies2, const AttrMap *attmap)
Definition index.c:2555
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition indexcmds.c:4476
Oid get_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 16318 of file tablecmds.c.

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

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

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

◆ ATExecClusterOn()

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

Definition at line 16694 of file tablecmds.c.

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

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:8878
static ObjectAddress ATExecColumnDefault(Relation rel, const char *colName, Node *newDefault, LOCKMODE lockmode)
Definition tablecmds.c:8215
static ObjectAddress ATExecDropIdentity(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode, bool recurse, bool recursing)
Definition tablecmds.c:8577
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:7831
static ObjectAddress ATExecAddConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *newConstraint, bool recurse, bool is_readd, LOCKMODE lockmode)
Definition tablecmds.c:9873
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:9127
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:9360
static void ATExecSetRowSecurity(Relation rel, bool rls)
static ObjectAddress ATExecCookedColumnDefault(Relation rel, AttrNumber attnum, Node *newDefault)
Definition tablecmds.c:8300
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:8460
static void ATExecGenericOptions(Relation rel, List *options)
static ObjectAddress ATExecSetStatistics(Relation rel, const char *colName, int16 colNum, Node *newValue, LOCKMODE lockmode)
Definition tablecmds.c:8982
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:8691
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:9781
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:9269
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:9696
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:9760
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 8215 of file tablecmds.c.

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

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

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

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

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

Referenced by ATExecCmd().

◆ ATExecDetachPartitionFinalize()

static ObjectAddress ATExecDetachPartitionFinalize ( Relation  rel,
RangeVar name 
)
static

Definition at line 21724 of file tablecmds.c.

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

16727{
16728 mark_index_clustered(rel, InvalidOid, false);
16729}

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

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

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

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

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

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

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

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

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

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

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

17471{
17472 EnableDisableRule(rel, rulename, fires_when);
17473
17475 RelationGetRelid(rel), 0);
17476}
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 17451 of file tablecmds.c.

17454{
17455 EnableDisableTrigger(rel, trigname, InvalidOid,
17456 fires_when, skip_system, recurse,
17457 lockmode);
17458
17460 RelationGetRelid(rel), 0);
17461}
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 18872 of file tablecmds.c.

18873{
18875 Oid relid;
18876 HeapTuple tuple;
18877
18878 relid = RelationGetRelid(rel);
18879
18881
18883
18884 if (!HeapTupleIsValid(tuple))
18885 elog(ERROR, "cache lookup failed for relation %u", relid);
18886
18888 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18889
18891 RelationGetRelid(rel), 0);
18892
18894 heap_freetuple(tuple);
18895}

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

18902{
18904 ForeignServer *server;
18906 HeapTuple tuple;
18907 bool isnull;
18911 Datum datum;
18913
18914 if (options == NIL)
18915 return;
18916
18918
18920 ObjectIdGetDatum(rel->rd_id));
18921 if (!HeapTupleIsValid(tuple))
18922 ereport(ERROR,
18924 errmsg("foreign table \"%s\" does not exist",
18927 server = GetForeignServer(tableform->ftserver);
18928 fdw = GetForeignDataWrapper(server->fdwid);
18929
18930 memset(repl_val, 0, sizeof(repl_val));
18931 memset(repl_null, false, sizeof(repl_null));
18932 memset(repl_repl, false, sizeof(repl_repl));
18933
18934 /* Extract the current options */
18936 tuple,
18938 &isnull);
18939 if (isnull)
18940 datum = PointerGetDatum(NULL);
18941
18942 /* Transform the options */
18944 datum,
18945 options,
18946 fdw->fdwvalidator);
18947
18948 if (DatumGetPointer(datum) != NULL)
18950 else
18952
18954
18955 /* Everything looks good - update the tuple */
18956
18959
18960 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
18961
18962 /*
18963 * Invalidate relcache so that all sessions will refresh any cached plans
18964 * that might depend on the old options.
18965 */
18967
18969 RelationGetRelid(rel), 0);
18970
18972
18973 heap_freetuple(tuple);
18974}

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

23009{
23014 Oid ownerId = InvalidOid;
23015 Oid save_userid;
23016 int save_sec_context;
23017 int save_nestlevel;
23018
23019 /*
23020 * Check ownership of merged partitions - partitions with different owners
23021 * cannot be merged. Also, collect the OIDs of these partitions during the
23022 * check.
23023 */
23025 {
23027
23028 /*
23029 * We are going to detach and remove this partition. We already took
23030 * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
23031 * NoLock is fine.
23032 */
23035
23036 if (OidIsValid(ownerId))
23037 {
23038 /* Do the partitions being merged have different owners? */
23039 if (ownerId != mergingPartition->rd_rel->relowner)
23040 ereport(ERROR,
23042 errmsg("partitions being merged have different owners"));
23043 }
23044 else
23045 ownerId = mergingPartition->rd_rel->relowner;
23046
23047 /* Store the next merging partition into the list. */
23050
23052 }
23053
23054 /* Look up the existing relation by the new partition name. */
23056
23057 /*
23058 * Check if this name is already taken. This helps us to detect the
23059 * situation when one of the merging partitions has the same name as the
23060 * new partition. Otherwise, this would fail later on anyway, but
23061 * catching this here allows us to emit a nicer error message.
23062 */
23064 {
23066 {
23067 /*
23068 * The new partition has the same name as one of the merging
23069 * partitions.
23070 */
23071 char tmpRelName[NAMEDATALEN];
23072
23073 /* Generate a temporary name. */
23074 sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23075
23076 /*
23077 * Rename the existing partition with a temporary name, leaving it
23078 * free for the new partition. We don't need to care about this
23079 * in the future because we're going to eventually drop the
23080 * existing partition anyway.
23081 */
23083
23084 /*
23085 * We must bump the command counter to make the new partition
23086 * tuple visible for rename.
23087 */
23089 }
23090 else
23091 {
23092 ereport(ERROR,
23094 errmsg("relation \"%s\" already exists", cmd->name->relname));
23095 }
23096 }
23097
23100
23101 /* Detach all merging partitions. */
23103 {
23105
23107
23109
23111 }
23112
23113 /*
23114 * Perform a preliminary check to determine whether it's safe to drop all
23115 * merging partitions before we actually do so later. After merging rows
23116 * into the new partitions via MergePartitionsMoveRows, all old partitions
23117 * need to be dropped. However, since the drop behavior is DROP_RESTRICT
23118 * and the merge process (MergePartitionsMoveRows) can be time-consuming,
23119 * performing an early check on the drop eligibility of old partitions is
23120 * preferable.
23121 */
23123 {
23124 ObjectAddress object;
23125
23126 /* Get oid of the later to be dropped relation. */
23128 object.classId = RelationRelationId;
23129 object.objectSubId = 0;
23130
23132 }
23133
23134 /*
23135 * Create a table for the new partition, using the partitioned table as a
23136 * model.
23137 */
23138 Assert(OidIsValid(ownerId));
23139 newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
23140
23141 /*
23142 * Switch to the table owner's userid, so that any index functions are run
23143 * as that user. Also, lockdown security-restricted operations and
23144 * arrange to make GUC variable changes local to this command.
23145 *
23146 * Need to do it after determining the namespace in the
23147 * createPartitionTable() call.
23148 */
23149 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23150 SetUserIdAndSecContext(ownerId,
23151 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23152 save_nestlevel = NewGUCNestLevel();
23154
23155 /* Copy data from merged partitions to the new partition. */
23157
23158 /* Drop the current partitions before attaching the new one. */
23160 {
23161 ObjectAddress object;
23162
23164 object.classId = RelationRelationId;
23165 object.objectSubId = 0;
23166
23167 performDeletion(&object, DROP_RESTRICT, 0);
23168 }
23169
23171
23172 /*
23173 * Attach a new partition to the partitioned table. wqueue = NULL:
23174 * verification for each cloned constraint is not needed.
23175 */
23177
23178 /* Keep the lock until commit. */
23180
23181 /* Roll back any GUC changes executed by index functions. */
23182 AtEOXact_GUC(false, save_nestlevel);
23183
23184 /* Restore the userid and security context. */
23185 SetUserIdAndSecContext(save_userid, save_sec_context);
23186}
void performDeletionCheck(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition dependency.c:338
int MyProcPid
Definition globals.c:49
int NewGUCNestLevel(void)
Definition guc.c:2142
void RestrictSearchPath(void)
Definition guc.c:2153
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition guc.c:2169
#define SECURITY_RESTRICTED_OPERATION
Definition miscadmin.h:331
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition miscinit.c:613
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition miscinit.c:620
#define sprintf
Definition port.h:262
List * partlist
Relation table_openrv_extended(const RangeVar *relation, LOCKMODE lockmode, bool missing_ok)
Definition table.c:103
static void MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
static 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 18728 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecSetAccessMethodNoStorage()

static void ATExecSetAccessMethodNoStorage ( Relation  rel,
Oid  newAccessMethodId 
)
static

Definition at line 16772 of file tablecmds.c.

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

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

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

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

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

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

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

9129{
9131 HeapTuple tuple,
9132 newtuple;
9135 Datum datum,
9136 newOptions;
9137 bool isnull;
9138 ObjectAddress address;
9142
9144
9146
9147 if (!HeapTupleIsValid(tuple))
9148 ereport(ERROR,
9150 errmsg("column \"%s\" of relation \"%s\" does not exist",
9153
9154 attnum = attrtuple->attnum;
9155 if (attnum <= 0)
9156 ereport(ERROR,
9158 errmsg("cannot alter system column \"%s\"",
9159 colName)));
9160
9161 /* Generate new proposed attoptions (text array) */
9163 &isnull);
9164 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9166 false, isReset);
9167 /* Validate new options */
9169
9170 /* Build new tuple. */
9171 memset(repl_null, false, sizeof(repl_null));
9172 memset(repl_repl, false, sizeof(repl_repl));
9173 if (newOptions != (Datum) 0)
9175 else
9180
9181 /* Update system catalog. */
9182 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9183
9185 RelationGetRelid(rel),
9186 attrtuple->attnum);
9188 RelationGetRelid(rel), attnum);
9189
9190 heap_freetuple(newtuple);
9191
9192 ReleaseSysCache(tuple);
9193
9195
9196 return address;
9197}
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 16892 of file tablecmds.c.

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

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

Referenced by ATExecCmd().

◆ ATExecSetRowSecurity()

static void ATExecSetRowSecurity ( Relation  rel,
bool  rls 
)
static

Definition at line 18842 of file tablecmds.c.

18843{
18845 Oid relid;
18846 HeapTuple tuple;
18847
18848 relid = RelationGetRelid(rel);
18849
18850 /* Pull the record for this relation and update it */
18852
18854
18855 if (!HeapTupleIsValid(tuple))
18856 elog(ERROR, "cache lookup failed for relation %u", relid);
18857
18859 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
18860
18862 RelationGetRelid(rel), 0);
18863
18865 heap_freetuple(tuple);
18866}

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

8983{
8984 int newtarget = 0;
8985 bool newtarget_default;
8987 HeapTuple tuple,
8988 newtuple;
8991 ObjectAddress address;
8995
8996 /*
8997 * We allow referencing columns by numbers only for indexes, since table
8998 * column numbers could contain gaps if columns are later dropped.
8999 */
9000 if (rel->rd_rel->relkind != RELKIND_INDEX &&
9001 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
9002 !colName)
9003 ereport(ERROR,
9005 errmsg("cannot refer to non-index column by number")));
9006
9007 /* -1 was used in previous versions for the default setting */
9008 if (newValue && intVal(newValue) != -1)
9009 {
9011 newtarget_default = false;
9012 }
9013 else
9014 newtarget_default = true;
9015
9016 if (!newtarget_default)
9017 {
9018 /*
9019 * Limit target to a sane range
9020 */
9021 if (newtarget < 0)
9022 {
9023 ereport(ERROR,
9025 errmsg("statistics target %d is too low",
9026 newtarget)));
9027 }
9029 {
9033 errmsg("lowering statistics target to %d",
9034 newtarget)));
9035 }
9036 }
9037
9039
9040 if (colName)
9041 {
9043
9044 if (!HeapTupleIsValid(tuple))
9045 ereport(ERROR,
9047 errmsg("column \"%s\" of relation \"%s\" does not exist",
9049 }
9050 else
9051 {
9053
9054 if (!HeapTupleIsValid(tuple))
9055 ereport(ERROR,
9057 errmsg("column number %d of relation \"%s\" does not exist",
9059 }
9060
9062
9063 attnum = attrtuple->attnum;
9064 if (attnum <= 0)
9065 ereport(ERROR,
9067 errmsg("cannot alter system column \"%s\"",
9068 colName)));
9069
9070 /*
9071 * Prevent this as long as the ANALYZE code skips virtual generated
9072 * columns.
9073 */
9074 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
9075 ereport(ERROR,
9077 errmsg("cannot alter statistics on virtual generated column \"%s\"",
9078 colName)));
9079
9080 if (rel->rd_rel->relkind == RELKIND_INDEX ||
9081 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
9082 {
9083 if (attnum > rel->rd_index->indnkeyatts)
9084 ereport(ERROR,
9086 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9087 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9088 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9089 ereport(ERROR,
9091 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9092 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9093 errhint("Alter statistics on table column instead.")));
9094 }
9095
9096 /* Build new tuple. */
9097 memset(repl_null, false, sizeof(repl_null));
9098 memset(repl_repl, false, sizeof(repl_repl));
9099 if (!newtarget_default)
9101 else
9106 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9107
9109 RelationGetRelid(rel),
9110 attrtuple->attnum);
9112 RelationGetRelid(rel), attnum);
9113
9114 heap_freetuple(newtuple);
9115
9116 ReleaseSysCache(tuple);
9117
9119
9120 return address;
9121}
static Datum Int16GetDatum(int16 X)
Definition postgres.h:172
HeapTuple SearchSysCacheAttNum(Oid relid, int16 attnum)
Definition syscache.c:539
#define MAX_STATISTICS_TARGET
Definition vacuum.h:350
#define intVal(v)
Definition value.h:79

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

Referenced by ATExecCmd().

◆ ATExecSetStorage()

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

Definition at line 9269 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

◆ ATParseTransformCmd()

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

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

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

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

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

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

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

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

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

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

17485{
17486 if (child_rel->rd_rel->reloftype)
17487 ereport(ERROR,
17489 errmsg("cannot change inheritance of typed table")));
17490
17491 if (child_rel->rd_rel->relispartition)
17492 ereport(ERROR,
17494 errmsg("cannot change inheritance of a partition")));
17495}

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

Referenced by ATPrepCmd().

◆ ATPrepChangePersistence()

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

Definition at line 19059 of file tablecmds.c.

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

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

Referenced by ATPrepCmd().

◆ ATPrepCmd()

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

Definition at line 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:6925
#define ATT_INDEX
Definition tablecmds.c:339
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition tablecmds.c:8832
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:9332
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:7257
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:6880
#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:9575
#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 9332 of file tablecmds.c.

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

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

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

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

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

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

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

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

Referenced by ATController().

◆ ATRewriteTable()

static void ATRewriteTable ( AlteredTableInfo tab,
Oid  OIDNewHeap 
)
static

Definition at line 6185 of file tablecmds.c.

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

References AccessExclusiveLock, Assert, CompactAttribute::attisdropped, CompactAttribute::attnullability, ATTNULLABLE_VALID, attnum, CHECK_FOR_INTERRUPTS, CheckRelationOidLockedByMe(), CONSTR_CHECK, CONSTR_FOREIGN, CONSTR_NOTNULL, AlteredTableInfo::constraints, NewConstraint::contype, CreateExecutorState(), DEBUG1, ExprContext::ecxt_scantuple, elog, ereport, errcode(), errmsg, errmsg_internal(), ERROR, errtable(), errtablecol(), errtableconstraint(), EState::es_instrument, EState::es_query_cxt, ExecCheck(), ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecEvalExpr(), ExecInitExpr(), ExecPrepareExpr(), ExecRelGenVirtualNotNull(), ExecStoreAllNullTuple(), ExecStoreVirtualTuple(), expand_generated_columns_in_expr(), fb(), foreach_int, ForwardScanDirection, FreeBulkInsertState(), FreeExecutorState(), GetBulkInsertState(), GetCurrentCommandId(), GetLatestSnapshot(), GetPerTupleExprContext, GetPerTupleMemoryContext, i, InitResultRelInfo(), InvalidAttrNumber, lappend_int(), lfirst, lfirst_int, makeNode, MakeSingleTupleTableSlot(), memcpy(), MemoryContextSwitchTo(), NewConstraint::name, NameStr, AlteredTableInfo::newvals, NIL, NoLock, OidIsValid, AlteredTableInfo::oldDesc, AlteredTableInfo::partition_constraint, NewConstraint::qual, NewConstraint::qualstate, RegisterSnapshot(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, AlteredTableInfo::relid, ResetExprContext, AlteredTableInfo::rewrite, slot_attisnull(), slot_getallattrs(), SO_NONE, table_beginscan(), table_close(), table_endscan(), table_finish_bulk_insert(), TABLE_INSERT_SKIP_FSM, table_open(), table_scan_getnextslot(), table_slot_callbacks(), table_tuple_insert(), TransferPredicateLocksToHeapRelation(), TupleDescAttr(), TupleDescCompactAttr(), UnregisterSnapshot(), AlteredTableInfo::validate_default, and AlteredTableInfo::verify_new_notnull.

Referenced by ATRewriteTables().

◆ ATRewriteTables()

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

Definition at line 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 true, /* reindex */
6062 RecentXmin,
6064 persistence);
6065
6067 }
6068 else if (tab->rewrite > 0 && tab->relkind == RELKIND_SEQUENCE)
6069 {
6070 if (tab->chgPersistence)
6072 }
6073 else
6074 {
6075 /*
6076 * If required, test the current data within the table against new
6077 * constraints generated by ALTER TABLE commands, but don't
6078 * rebuild data.
6079 */
6080 if (tab->constraints != NIL || tab->verify_new_notnull ||
6081 tab->partition_constraint != NULL)
6083
6084 /*
6085 * If we had SET TABLESPACE but no reason to reconstruct tuples,
6086 * just do a block-by-block copy.
6087 */
6088 if (tab->newTableSpace)
6089 ATExecSetTableSpace(tab->relid, tab->newTableSpace, lockmode);
6090 }
6091
6092 /*
6093 * Also change persistence of owned sequences, so that it matches the
6094 * table persistence.
6095 */
6096 if (tab->chgPersistence)
6097 {
6099 ListCell *lc;
6100
6101 foreach(lc, seqlist)
6102 {
6104
6106 }
6107 }
6108 }
6109
6110 /*
6111 * Foreign key constraints are checked in a final pass, since (a) it's
6112 * generally best to examine each one separately, and (b) it's at least
6113 * theoretically possible that we have changed both relations of the
6114 * foreign key, and we'd better have finished both rewrites before we try
6115 * to read the tables.
6116 */
6117 foreach(ltab, *wqueue)
6118 {
6120 Relation rel = NULL;
6121 ListCell *lcon;
6122
6123 /* Relations without storage may be ignored here too */
6124 if (!RELKIND_HAS_STORAGE(tab->relkind))
6125 continue;
6126
6127 foreach(lcon, tab->constraints)
6128 {
6129 NewConstraint *con = lfirst(lcon);
6130
6131 if (con->contype == CONSTR_FOREIGN)
6132 {
6135
6136 if (rel == NULL)
6137 {
6138 /* Long since locked, no need for another */
6139 rel = table_open(tab->relid, NoLock);
6140 }
6141
6143
6145 con->refindid,
6146 con->conid,
6147 con->conwithperiod);
6148
6149 /*
6150 * No need to mark the constraint row as validated, we did
6151 * that when we inserted the row earlier.
6152 */
6153
6155 }
6156 }
6157
6158 if (rel)
6159 table_close(rel, NoLock);
6160 }
6161
6162 /* Finally, run any afterStmts that were queued up */
6163 foreach(ltab, *wqueue)
6164 {
6166 ListCell *lc;
6167
6168 foreach(lc, tab->afterStmts)
6169 {
6170 Node *stmt = (Node *) lfirst(lc);
6171
6174 }
6175 }
6176}
void SequenceChangePersistence(Oid relid, char newrelpersistence)
Definition sequence.c:542
void EventTriggerTableRewrite(Node *parsetree, Oid tableOid, int reason)
#define RowShareLock
Definition lockdefs.h:37
MultiXactId ReadNextMultiXactId(void)
Definition multixact.c:679
List * getOwnedSequences(Oid relid)
Definition pg_depend.c:1009
#define RelationIsUsedAsCatalogTable(relation)
Definition rel.h:399
void finish_heap_swap(Oid OIDOldHeap, Oid OIDNewHeap, bool is_system_catalog, bool swap_toast_by_content, bool check_constraints, bool is_internal, bool reindex, TransactionId frozenXid, MultiXactId cutoffMulti, char newrelpersistence)
Definition repack.c:1865
Oid make_new_heap(Oid OIDOldHeap, Oid NewTableSpace, Oid NewAccessMethod, char relpersistence, LOCKMODE lockmode)
Definition repack.c:1108
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:6185

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

6804{
6805 int actual_target;
6806
6807 switch (rel->rd_rel->relkind)
6808 {
6809 case RELKIND_RELATION:
6811 break;
6814 break;
6815 case RELKIND_VIEW:
6817 break;
6818 case RELKIND_MATVIEW:
6820 break;
6821 case RELKIND_INDEX:
6823 break;
6826 break;
6829 break;
6832 break;
6833 case RELKIND_SEQUENCE:
6835 break;
6836 default:
6837 actual_target = 0;
6838 break;
6839 }
6840
6841 /* Wrong target type? */
6842 if ((actual_target & allowed_targets) == 0)
6843 {
6844 const char *action_str = alter_table_type_to_string(cmdtype);
6845
6846 if (action_str)
6847 ereport(ERROR,
6849 /* translator: %s is a group of some SQL keywords */
6850 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6852 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6853 else
6854 /* internal error? */
6855 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6857 }
6858
6859 /* Permissions checks */
6863
6865 ereport(ERROR,
6867 errmsg("permission denied: \"%s\" is a system catalog",
6869}
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition tablecmds.c:6656

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

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

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

20878{
20879 List *idxes;
20883 ListCell *cell;
20884 MemoryContext cxt;
20886
20888 "AttachPartitionEnsureIndexes",
20891
20896
20897 /* Build arrays of all existing indexes and their IndexInfos */
20899 {
20901
20904 }
20905
20906 /*
20907 * If we're attaching a foreign table, we must fail if any of the indexes
20908 * is a constraint index; otherwise, there's nothing to do here. Do this
20909 * before starting work, to avoid wasting the effort of building a few
20910 * non-unique indexes before coming across a unique one.
20911 */
20912 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
20913 {
20914 foreach(cell, idxes)
20915 {
20916 Oid idx = lfirst_oid(cell);
20918
20919 if (idxRel->rd_index->indisunique ||
20920 idxRel->rd_index->indisprimary)
20921 ereport(ERROR,
20923 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
20926 errdetail("Partitioned table \"%s\" contains unique indexes.",
20929 }
20930
20931 goto out;
20932 }
20933
20934 /*
20935 * For each index on the partitioned table, find a matching one in the
20936 * partition-to-be; if one is not found, create one.
20937 */
20938 foreach(cell, idxes)
20939 {
20940 Oid idx = lfirst_oid(cell);
20942 IndexInfo *info;
20943 AttrMap *attmap;
20944 bool found = false;
20946
20947 /*
20948 * Ignore indexes in the partitioned table other than partitioned
20949 * indexes.
20950 */
20951 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
20952 {
20954 continue;
20955 }
20956
20957 /* construct an indexinfo to compare existing indexes against */
20958 info = BuildIndexInfo(idxRel);
20960 RelationGetDescr(rel),
20961 false);
20963
20964 /*
20965 * Scan the list of existing indexes in the partition-to-be, and mark
20966 * the first matching, valid, unattached one we find, if any, as
20967 * partition of the parent index. If we find one, we're done.
20968 */
20969 for (int i = 0; i < list_length(attachRelIdxs); i++)
20970 {
20973
20974 /* does this index have a parent? if so, can't use it */
20975 if (attachrelIdxRels[i]->rd_rel->relispartition)
20976 continue;
20977
20978 /* If this index is invalid, can't use it */
20979 if (!attachrelIdxRels[i]->rd_index->indisvalid)
20980 continue;
20981
20982 if (CompareIndexInfo(attachInfos[i], info,
20983 attachrelIdxRels[i]->rd_indcollation,
20984 idxRel->rd_indcollation,
20985 attachrelIdxRels[i]->rd_opfamily,
20986 idxRel->rd_opfamily,
20987 attmap))
20988 {
20989 /*
20990 * If this index is being created in the parent because of a
20991 * constraint, then the child needs to have a constraint also,
20992 * so look for one. If there is no such constraint, this
20993 * index is no good, so keep looking.
20994 */
20996 {
20997 cldConstrOid =
20999 cldIdxId);
21000 /* no dice */
21002 continue;
21003
21004 /* Ensure they're both the same type of constraint */
21007 continue;
21008 }
21009
21010 /* bingo. */
21015 found = true;
21016
21018 break;
21019 }
21020 }
21021
21022 /*
21023 * If no suitable index was found in the partition-to-be, create one
21024 * now. Note that if this is a PK, not-null constraints must already
21025 * exist.
21026 */
21027 if (!found)
21028 {
21029 IndexStmt *stmt;
21030 Oid conOid;
21031
21033 idxRel, attmap,
21034 &conOid);
21038 conOid,
21039 -1,
21040 true, false, false, false, false);
21041 }
21042
21044 }
21045
21046out:
21047 /* Clean up. */
21048 for (int i = 0; i < list_length(attachRelIdxs); i++)
21052}
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:435

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

Referenced by attachPartitionTable().

◆ AttachPartitionForeignKey()

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

Definition at line 11867 of file tablecmds.c.

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

20498{
20499 /*
20500 * Create an inheritance; the relevant checks are performed inside the
20501 * function.
20502 */
20503 CreateInheritance(attachrel, rel, true);
20504
20505 /* Update the pg_class entry. */
20506 StorePartitionBound(attachrel, rel, bound);
20507
20508 /* Ensure there exists a correct set of indexes in the partition. */
20510
20511 /* and triggers */
20513
20514 /*
20515 * Clone foreign key constraints. Callee is responsible for setting up
20516 * for phase 3 constraint verification.
20517 */
20519}
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition heap.c:4054
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 6955 of file tablecmds.c.

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

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:775
char identity
Definition parsenodes.h:781
char * storage_name
Definition parsenodes.h:778
char * colname
Definition parsenodes.h:770
TypeName * typeName
Definition parsenodes.h:771
char generated
Definition parsenodes.h:784
char storage
Definition parsenodes.h:777
bool is_local
Definition parsenodes.h:774
int16 inhcount
Definition parsenodes.h:773
char * compression
Definition parsenodes.h:772
bool setof
Definition parsenodes.h:290
TupleDesc CreateTemplateTupleDesc(int natts)
Definition tupdesc.c:165
void TupleDescFinalize(TupleDesc tupdesc)
Definition tupdesc.c:511
void populate_compact_attribute(TupleDesc tupdesc, int attnum)
Definition tupdesc.c:100
void TupleDescInitEntryCollation(TupleDesc desc, AttrNumber attributeNumber, Oid collationid)
Definition tupdesc.c:1084
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition tupdesc.c:900

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

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

◆ buildExpressionExecutionStates()

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

Definition at line 22375 of file tablecmds.c.

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

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

16561{
16563 SysScanDesc scan;
16564 ScanKeyData key[1];
16566
16568 ScanKeyInit(&key[0],
16571 ObjectIdGetDatum(relationOid));
16573 true, NULL, 1, key);
16575 {
16580 Acl *newAcl;
16582 bool isNull;
16583 HeapTuple newtuple;
16584
16585 /* Ignore dropped columns */
16586 if (att->attisdropped)
16587 continue;
16588
16592 &isNull);
16593 /* Null ACLs do not require changes */
16594 if (isNull)
16595 continue;
16596
16597 memset(repl_null, false, sizeof(repl_null));
16598 memset(repl_repl, false, sizeof(repl_repl));
16599
16604
16608
16609 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16610
16611 heap_freetuple(newtuple);
16612 }
16613 systable_endscan(scan);
16615}

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

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

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

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

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

Referenced by ATExecAddColumn(), and renameatt_internal().

◆ check_of_type()

void check_of_type ( HeapTuple  typetuple)

Definition at line 7207 of file tablecmds.c.

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

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

13912{
13913 Oid roleid = GetUserId();
13915 int i;
13916
13917 /* Okay if we have relation-level REFERENCES permission */
13920 if (aclresult == ACLCHECK_OK)
13921 return;
13922 /* Else we must have REFERENCES on each column */
13923 for (i = 0; i < natts; i++)
13924 {
13926 roleid, ACL_REFERENCES);
13927 if (aclresult != ACLCHECK_OK)
13930 }
13931}
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:565

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

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

◆ CheckTableNotInUse()

void CheckTableNotInUse ( Relation  rel,
const char stmt 
)

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

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

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

◆ ChooseForeignKeyConstraintNameAddition()

static char * ChooseForeignKeyConstraintNameAddition ( List colnames)
static

Definition at line 9947 of file tablecmds.c.

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

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

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

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

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

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

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

Referenced by attachPartitionTable(), and DefineRelation().

◆ ComputePartitionAttrs()

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

Definition at line 20028 of file tablecmds.c.

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

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

Referenced by DefineRelation().

◆ ConstraintImpliedByRelConstraint()

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

Definition at line 20357 of file tablecmds.c.

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

17715{
17718
17719 if (acon->condeferrable != bcon->condeferrable ||
17720 acon->condeferred != bcon->condeferred ||
17723 return false;
17724 else
17725 return true;
17726}
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 14040 of file tablecmds.c.

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

14107{
14110
14111 /*
14112 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14113 * DELETE action on the referenced table.
14114 */
14116 fk_trigger->replace = false;
14117 fk_trigger->isconstraint = true;
14118 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14119 fk_trigger->relation = NULL;
14120 fk_trigger->args = NIL;
14121 fk_trigger->row = true;
14124 fk_trigger->columns = NIL;
14125 fk_trigger->whenClause = NULL;
14126 fk_trigger->transitionRels = NIL;
14127 fk_trigger->constrrel = NULL;
14128
14129 switch (fkconstraint->fk_del_action)
14130 {
14132 fk_trigger->deferrable = fkconstraint->deferrable;
14133 fk_trigger->initdeferred = fkconstraint->initdeferred;
14134 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
14135 break;
14137 fk_trigger->deferrable = false;
14138 fk_trigger->initdeferred = false;
14139 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
14140 break;
14142 fk_trigger->deferrable = false;
14143 fk_trigger->initdeferred = false;
14144 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
14145 break;
14147 fk_trigger->deferrable = false;
14148 fk_trigger->initdeferred = false;
14149 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
14150 break;
14152 fk_trigger->deferrable = false;
14153 fk_trigger->initdeferred = false;
14154 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
14155 break;
14156 default:
14157 elog(ERROR, "unrecognized FK action type: %d",
14158 (int) fkconstraint->fk_del_action);
14159 break;
14160 }
14161
14163 constraintOid, indexOid, InvalidOid,
14164 parentDelTrigger, NULL, true, false);
14165 if (deleteTrigOid)
14166 *deleteTrigOid = trigAddress.objectId;
14167
14168 /* Make changes-so-far visible */
14170
14171 /*
14172 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14173 * UPDATE action on the referenced table.
14174 */
14176 fk_trigger->replace = false;
14177 fk_trigger->isconstraint = true;
14178 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14179 fk_trigger->relation = NULL;
14180 fk_trigger->args = NIL;
14181 fk_trigger->row = true;
14184 fk_trigger->columns = NIL;
14185 fk_trigger->whenClause = NULL;
14186 fk_trigger->transitionRels = NIL;
14187 fk_trigger->constrrel = NULL;
14188
14189 switch (fkconstraint->fk_upd_action)
14190 {
14192 fk_trigger->deferrable = fkconstraint->deferrable;
14193 fk_trigger->initdeferred = fkconstraint->initdeferred;
14194 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
14195 break;
14197 fk_trigger->deferrable = false;
14198 fk_trigger->initdeferred = false;
14199 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
14200 break;
14202 fk_trigger->deferrable = false;
14203 fk_trigger->initdeferred = false;
14204 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
14205 break;
14207 fk_trigger->deferrable = false;
14208 fk_trigger->initdeferred = false;
14209 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
14210 break;
14212 fk_trigger->deferrable = false;
14213 fk_trigger->initdeferred = false;
14214 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
14215 break;
14216 default:
14217 elog(ERROR, "unrecognized FK action type: %d",
14218 (int) fkconstraint->fk_upd_action);
14219 break;
14220 }
14221
14223 constraintOid, indexOid, InvalidOid,
14224 parentUpdTrigger, NULL, true, false);
14225 if (updateTrigOid)
14226 *updateTrigOid = trigAddress.objectId;
14227}
#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 14238 of file tablecmds.c.

14243{
14245 constraintOid, indexOid,
14246 parentInsTrigger, true);
14248 constraintOid, indexOid,
14249 parentUpdTrigger, false);
14250}
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 17617 of file tablecmds.c.

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

22720{
22722 Oid newRelId;
22725 List *colList = NIL;
22726 Oid relamId;
22730
22731 /* If the existing rel is temp, it must belong to this session. */
22733 ereport(ERROR,
22735 errmsg("cannot create as partition of temporary relation of another session"));
22736
22737 /* Look up inheritance ancestors and generate the relation schema. */
22739
22740 /* Create a tuple descriptor from the relation schema. */
22742
22743 /* Look up the access method for the new relation. */
22745
22746 /* Look up the namespace in which we are supposed to create the relation. */
22747 namespaceId =
22750 ereport(ERROR,
22752 errmsg("relation \"%s\" already exists", newPartName->relname));
22753
22754 /*
22755 * We intended to create the partition with the same persistence as the
22756 * parent table, but we still need to recheck because that might be
22757 * affected by the search_path. If the parent is permanent, so must be
22758 * all of its partitions.
22759 */
22760 if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22761 newPartName->relpersistence == RELPERSISTENCE_TEMP)
22762 ereport(ERROR,
22764 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
22766
22767 /* Permanent rels cannot be partitions belonging to a temporary parent. */
22768 if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
22769 parent_relform->relpersistence == RELPERSISTENCE_TEMP)
22770 ereport(ERROR,
22772 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
22774
22775 /* Create the relation. */
22778 parent_relform->reltablespace,
22779 InvalidOid,
22780 InvalidOid,
22781 InvalidOid,
22782 ownerId,
22783 relamId,
22784 descriptor,
22785 NIL,
22787 newPartName->relpersistence,
22788 false,
22789 false,
22791 (Datum) 0,
22792 true,
22794 true,
22795 InvalidOid,
22796 NULL);
22797
22798 /*
22799 * We must bump the command counter to make the newly-created relation
22800 * tuple visible for opening.
22801 */
22803
22804 /*
22805 * Open the new partition with no lock, because we already have an
22806 * AccessExclusiveLock placed there after creation.
22807 */
22809
22810 /* Find or create a work queue entry for the newly created table. */
22812
22813 /* Create constraints, default values, and generated values. */
22815
22816 /*
22817 * Need to call CommandCounterIncrement, so a fresh relcache entry has
22818 * newly installed constraint info.
22819 */
22821
22822 return newRel;
22823}
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 23207 of file tablecmds.c.

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

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

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

Referenced by createPartitionTable().

◆ decompile_conbin()

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

Definition at line 17686 of file tablecmds.c.

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

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

Referenced by constraints_equivalent().

◆ DefineRelation()

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

Definition at line 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:3898
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,
uint32  ti_options 
)
static

Definition at line 23230 of file tablecmds.c.

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

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

22987{
22988 /* Remove the pg_inherits row first. */
22990
22991 /*
22992 * Detaching the partition might involve TOAST table access, so ensure we
22993 * have a valid snapshot.
22994 */
22996
22997 /* Do the final part of detaching. */
22999
23001}

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

18404{
18406 SysScanDesc scan;
18407 ScanKeyData key[3];
18409
18411
18412 ScanKeyInit(&key[0],
18416 ScanKeyInit(&key[1],
18419 ObjectIdGetDatum(relid));
18420 ScanKeyInit(&key[2],
18423 Int32GetDatum(0));
18424
18426 NULL, 3, key);
18427
18429 {
18431
18432 if (dep->refclassid == refclassid &&
18433 dep->refobjid == refobjid &&
18434 dep->refobjsubid == 0 &&
18435 dep->deptype == deptype)
18437 }
18438
18439 systable_endscan(scan);
18441}

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

21759{
21761 SysScanDesc scan;
21764 ObjectAddresses *objects;
21765
21766 objects = new_object_addresses();
21767
21768 /*
21769 * Scan pg_trigger to search for all triggers on this rel.
21770 */
21775 true, NULL, 1, &skey);
21777 {
21780
21781 /* Ignore triggers that weren't cloned */
21782 if (!OidIsValid(pg_trigger->tgparentid))
21783 continue;
21784
21785 /*
21786 * Ignore internal triggers that are implementation objects of foreign
21787 * keys, because these will be detached when the foreign keys
21788 * themselves are.
21789 */
21790 if (OidIsValid(pg_trigger->tgconstrrelid))
21791 continue;
21792
21793 /*
21794 * This is ugly, but necessary: remove the dependency markings on the
21795 * trigger so that it can be removed.
21796 */
21803
21804 /* remember this trigger to remove it below */
21806 add_exact_object_address(&trig, objects);
21807 }
21808
21809 /* make the dependency removal visible to the deletion below */
21812
21813 /* done */
21814 free_object_addresses(objects);
21815 systable_endscan(scan);
21817}

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

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

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

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

◆ DropErrorMsgNonExistent()

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

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

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

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

22417{
22418 econtext->ecxt_scantuple = insertslot;
22419
22421 {
22422 if (!ex->is_generated)
22423 continue;
22424
22425 insertslot->tts_values[ex->attnum - 1]
22426 = ExecEvalExpr(ex->exprstate,
22427 econtext,
22428 &insertslot->tts_isnull[ex->attnum - 1]);
22429 }
22430
22432 {
22433 switch (con->contype)
22434 {
22435 case CONSTR_CHECK:
22436 if (!ExecCheck(con->qualstate, econtext))
22437 ereport(ERROR,
22439 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
22441 errtableconstraint(newPartRel, con->name));
22442 break;
22443 case CONSTR_NOTNULL:
22444 case CONSTR_FOREIGN:
22445 /* Nothing to do here */
22446 break;
22447 default:
22448 elog(ERROR, "unrecognized constraint type: %d",
22449 (int) con->contype);
22450 }
22451 }
22452}
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:712
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:740
void ResetSequence(Oid seq_relid)
Definition sequence.c:255
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition dynahash.c:889
HTAB * hash_create(const char *tabname, int64 nelem, const HASHCTL *info, int flags)
Definition dynahash.c:360
void hash_destroy(HTAB *hashp)
Definition dynahash.c:802
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition dynahash.c:1352
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition dynahash.c:1317
#define PG_TRY(...)
Definition elog.h:374
#define PG_END_TRY(...)
Definition elog.h:399
#define PG_FINALLY(...)
Definition elog.h:391
FdwRoutine * GetFdwRoutineByServerId(Oid serverid)
Definition foreign.c:409
Oid GetForeignServerIdByRelId(Oid relid)
Definition foreign.c:387
List * heap_truncate_find_FKs(List *relationIds)
Definition heap.c:3771
void heap_truncate_check_FKs(List *relations, bool tempTables)
Definition heap.c:3676
void heap_truncate_one_rel(Relation rel)
Definition heap.c:3632
#define XLOG_HEAP_TRUNCATE
Definition heapam_xlog.h:36
#define XLH_TRUNCATE_RESTART_SEQS
#define SizeOfHeapTruncate
#define XLH_TRUNCATE_CASCADE
@ HASH_ENTER
Definition hsearch.h:109
#define HASH_CONTEXT
Definition hsearch.h:97
#define HASH_ELEM
Definition hsearch.h:90
#define HASH_BLOBS
Definition hsearch.h:92
bool reindex_relation(const ReindexStmt *stmt, Oid relid, int flags, const ReindexParams *params)
Definition index.c:3969
#define REINDEX_REL_PROCESS_TOAST
Definition index.h:166
List * list_difference_ptr(const List *list1, const List *list2)
Definition list.c:1263
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:4349
void RelationSetNewRelfilenumber(Relation relation, char persistence)
Definition relcache.c:3765
List * es_opened_result_relations
Definition execnodes.h:724
ExecForeignTruncate_function ExecForeignTruncate
Definition fdwapi.h:268
Size keysize
Definition hsearch.h:69
Size entrysize
Definition hsearch.h:70
MemoryContext hcxt
Definition hsearch.h:81
SubTransactionId rd_newRelfilelocatorSubid
Definition rel.h:104
Relation ri_RelationDesc
Definition execnodes.h:513
static void truncate_check_perms(Oid relid, Form_pg_class reltuple)
Definition tablecmds.c: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:5161
void AfterTriggerBeginQuery(void)
Definition trigger.c:5141
void SwitchToUntrustedUser(Oid userid, UserContext *context)
Definition usercontext.c:33
void RestoreUserContext(UserContext *context)
Definition usercontext.c:87
SubTransactionId GetCurrentSubTransactionId(void)
Definition xact.c:793
#define XLogLogicalInfoActive()
Definition xlog.h:137
#define XLOG_INCLUDE_ORIGIN
Definition xlog.h:166
XLogRecPtr XLogInsert(RmgrId rmid, uint8 info)
Definition xloginsert.c:482
void XLogRegisterData(const void *data, uint32 len)
Definition xloginsert.c:372
void XLogSetRecordFlags(uint8 flags)
Definition xloginsert.c:464
void XLogBeginInsert(void)
Definition xloginsert.c:153

References AccessExclusiveLock, aclcheck_error(), ACLCHECK_NOT_OWNER, AfterTriggerBeginQuery(), AfterTriggerEndQuery(), Assert, CheckTableForSerializableConflictIn(), CreateExecutorState(), CurrentMemoryContext, DROP_CASCADE, DROP_RESTRICT, HASHCTL::entrysize, ereport, errmsg, EState::es_opened_result_relations, ExecASTruncateTriggers(), ExecBSTruncateTriggers(), FdwRoutine::ExecForeignTruncate, fb(), FreeExecutorState(), GetCurrentSubTransactionId(), GetFdwRoutineByServerId(), GetForeignServerIdByRelId(), getOwnedSequences(), GetUserId(), HASH_BLOBS, HASH_CONTEXT, hash_create(), hash_destroy(), HASH_ELEM, HASH_ENTER, hash_search(), hash_seq_init(), hash_seq_search(), HASHCTL::hcxt, heap_truncate_check_FKs(), heap_truncate_find_FKs(), heap_truncate_one_rel(), i, InitResultRelInfo(), HASHCTL::keysize, lappend(), lappend_oid(), lfirst, lfirst_oid, list_copy(), list_difference_ptr(), list_length(), MyDatabaseId, NIL, NoLock, NOTICE, object_ownercheck(), OBJECT_SEQUENCE, OidIsValid, palloc(), PG_END_TRY, PG_FINALLY, PG_TRY, pgstat_count_truncate(), RelationData::rd_createSubid, RelationData::rd_newRelfilelocatorSubid, RelationData::rd_rel, REINDEX_REL_PROCESS_TOAST, reindex_relation(), relation_close(), relation_open(), RelationGetRelationName, RelationGetRelid, RelationIsLogicallyLogged, RelationSetNewRelfilenumber(), ResetSequence(), RestoreUserContext(), ResultRelInfo::ri_RelationDesc, SizeOfHeapTruncate, SwitchToUntrustedUser(), table_close(), table_open(), truncate_check_activity(), truncate_check_perms(), truncate_check_rel(), XLH_TRUNCATE_CASCADE, XLH_TRUNCATE_RESTART_SEQS, XLOG_HEAP_TRUNCATE, XLOG_INCLUDE_ORIGIN, XLogBeginInsert(), XLogInsert(), XLogLogicalInfoActive, XLogRegisterData(), and XLogSetRecordFlags().

Referenced by apply_handle_truncate(), and ExecuteTruncate().

◆ find_composite_type_dependencies()

void find_composite_type_dependencies ( Oid  typeOid,
Relation  origRelation,
const char origTypeName 
)

Definition at line 7000 of file tablecmds.c.

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

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

7159{
7161 ScanKeyData key[1];
7162 TableScanDesc scan;
7163 HeapTuple tuple;
7164 List *result = NIL;
7165
7167
7168 ScanKeyInit(&key[0],
7171 ObjectIdGetDatum(typeOid));
7172
7173 scan = table_beginscan_catalog(classRel, 1, key);
7174
7175 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7176 {
7178
7179 if (behavior == DROP_RESTRICT)
7180 ereport(ERROR,
7182 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7183 typeName),
7184 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7185 else
7187 }
7188
7189 table_endscan(scan);
7191
7192 return result;
7193}
uint32 result

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

Referenced by ATTypedTableRecursion(), and renameatt_internal().

◆ findAttrByName()

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

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

13883{
13884 CoercionPathType ret;
13885
13887 {
13889 *funcid = InvalidOid;
13890 }
13891 else
13892 {
13894 COERCION_IMPLICIT, funcid);
13895 if (ret == COERCION_PATH_NONE)
13896 /* A previously-relied-upon cast is now gone. */
13897 elog(ERROR, "could not find cast from %u to %u",
13899 }
13900
13901 return ret;
13902}
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 22297 of file tablecmds.c.

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

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

22336{
22337 char cstorage = 0;
22338
22339 if (pg_strcasecmp(storagemode, "plain") == 0)
22341 else if (pg_strcasecmp(storagemode, "external") == 0)
22343 else if (pg_strcasecmp(storagemode, "extended") == 0)
22345 else if (pg_strcasecmp(storagemode, "main") == 0)
22347 else if (pg_strcasecmp(storagemode, "default") == 0)
22349 else
22350 ereport(ERROR,
22352 errmsg("invalid storage type \"%s\"",
22353 storagemode)));
22354
22355 /*
22356 * safety check: do not allow toasted storage modes unless column datatype
22357 * is TOAST-aware.
22358 */
22360 ereport(ERROR,
22362 errmsg("column data type %s can only have storage PLAIN",
22364
22365 return cstorage;
22366}
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 12148 of file tablecmds.c.

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

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

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

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

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

Referenced by ATExecSetTableSpace().

◆ MarkInheritDetached()

static void MarkInheritDetached ( Relation  child_rel,
Relation  parent_rel 
)
static

Definition at line 18105 of file tablecmds.c.

18106{
18108 SysScanDesc scan;
18111 bool found = false;
18112
18113 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
18114
18115 /*
18116 * Find pg_inherits entries by inhparent. (We need to scan them all in
18117 * order to verify that no other partition is pending detach.)
18118 */
18120 ScanKeyInit(&key,
18125 true, NULL, 1, &key);
18126
18128 {
18130
18132 if (inhForm->inhdetachpending)
18133 ereport(ERROR,
18135 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
18136 get_rel_name(inhForm->inhrelid),
18137 get_namespace_name(parent_rel->rd_rel->relnamespace),
18139 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
18140
18141 if (inhForm->inhrelid == RelationGetRelid(child_rel))
18142 {
18144
18147
18149 &inheritsTuple->t_self,
18150 newtup);
18151 found = true;
18153 /* keep looking, to ensure we catch others pending detach */
18154 }
18155 }
18156
18157 /* Done */
18158 systable_endscan(scan);
18160
18161 if (!found)
18162 ereport(ERROR,
18164 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18167}
#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 */
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:359
Node * cooked_default
Definition parsenodes.h:780
Node * raw_default
Definition parsenodes.h:779
static ColumnDef * MergeInheritedAttribute(List *inh_columns, int exist_attno, const ColumnDef *newdef)
Definition tablecmds.c: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 17743 of file tablecmds.c.

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

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

17882{
17887 Oid parent_relid = RelationGetRelid(parent_rel);
17888 AttrMap *attmap;
17889
17891
17892 /* Outer loop scans through the parent's constraint definitions */
17896 ObjectIdGetDatum(parent_relid));
17898 true, NULL, 1, &parent_key);
17899
17902 true);
17903
17905 {
17911 bool found = false;
17912
17913 if (parent_con->contype != CONSTRAINT_CHECK &&
17914 parent_con->contype != CONSTRAINT_NOTNULL)
17915 continue;
17916
17917 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
17918 if (parent_con->connoinherit)
17919 continue;
17920
17921 if (parent_con->contype == CONSTRAINT_NOTNULL)
17923 else
17925
17926 /* Search for a child constraint matching this one */
17932 true, NULL, 1, &child_key);
17933
17935 {
17938
17939 if (child_con->contype != parent_con->contype)
17940 continue;
17941
17942 /*
17943 * CHECK constraint are matched by constraint name, NOT NULL ones
17944 * by attribute number.
17945 */
17946 if (child_con->contype == CONSTRAINT_CHECK)
17947 {
17948 if (strcmp(NameStr(parent_con->conname),
17949 NameStr(child_con->conname)) != 0)
17950 continue;
17951 }
17952 else if (child_con->contype == CONSTRAINT_NOTNULL)
17953 {
17957
17960 if (parent_attno != attmap->attnums[child_attno - 1])
17961 continue;
17962
17964 /* there shouldn't be constraints on dropped columns */
17965 if (parent_attr->attisdropped || child_attr->attisdropped)
17966 elog(ERROR, "found not-null constraint on dropped columns");
17967 }
17968
17969 if (child_con->contype == CONSTRAINT_CHECK &&
17971 ereport(ERROR,
17973 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
17975
17976 /*
17977 * If the child constraint is "no inherit" then cannot merge
17978 */
17979 if (child_con->connoinherit)
17980 ereport(ERROR,
17982 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
17984
17985 /*
17986 * If the child constraint is "not valid" then cannot merge with a
17987 * valid parent constraint
17988 */
17989 if (parent_con->convalidated && child_con->conenforced &&
17990 !child_con->convalidated)
17991 ereport(ERROR,
17993 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
17995
17996 /*
17997 * A NOT ENFORCED child constraint cannot be merged with an
17998 * ENFORCED parent constraint. However, the reverse is allowed,
17999 * where the child constraint is ENFORCED.
18000 */
18001 if (parent_con->conenforced && !child_con->conenforced)
18002 ereport(ERROR,
18004 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
18006
18007 /*
18008 * OK, bump the child constraint's inheritance count. (If we fail
18009 * later on, this change will just roll back.)
18010 */
18013
18014 if (pg_add_s16_overflow(child_con->coninhcount, 1,
18015 &child_con->coninhcount))
18016 ereport(ERROR,
18018 errmsg("too many inheritance parents"));
18019
18020 /*
18021 * In case of partitions, an inherited constraint must be
18022 * inherited only once since it cannot have multiple parents and
18023 * it is never considered local.
18024 */
18025 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18026 {
18027 Assert(child_con->coninhcount == 1);
18028 child_con->conislocal = false;
18029 }
18030
18033
18034 found = true;
18035 break;
18036 }
18037
18039
18040 if (!found)
18041 {
18042 if (parent_con->contype == CONSTRAINT_NOTNULL)
18043 ereport(ERROR,
18045 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
18046 get_attname(parent_relid,
18048 false),
18050
18051 ereport(ERROR,
18053 errmsg("child table is missing constraint \"%s\"",
18054 NameStr(parent_con->conname))));
18055 }
18056 }
18057
18060}
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 22831 of file tablecmds.c.

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

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

Referenced by ATExecMergePartitions().

◆ NotNullImpliedByRelConstraints()

static bool NotNullImpliedByRelConstraints ( Relation  rel,
Form_pg_attribute  attr 
)
static

Definition at line 8178 of file tablecmds.c.

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

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

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

Referenced by check_default_partition_contents(), and QueuePartitionConstraintValidation().

◆ PreCommit_on_commit_actions()

void PreCommit_on_commit_actions ( void  )

Definition at line 19558 of file tablecmds.c.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

16091{
16092 CommentStmt *cmd;
16093 char *comment_str;
16095
16096 /* Look for comment for object wanted, and leave if none */
16098 if (comment_str == NULL)
16099 return;
16100
16101 /* Build CommentStmt node, copying all input data for safety */
16102 cmd = makeNode(CommentStmt);
16103 if (rel)
16104 {
16106 cmd->object = (Node *)
16109 makeString(pstrdup(conname)));
16110 }
16111 else
16112 {
16114 cmd->object = (Node *)
16116 makeString(pstrdup(conname)));
16117 }
16118 cmd->comment = comment_str;
16119
16120 /* Append it to list of commands */
16122 newcmd->subtype = AT_ReAddComment;
16123 newcmd->def = (Node *) cmd;
16124 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
16125}
TypeName * makeTypeNameFromNameList(List *names)
Definition makefuncs.c:531
@ OBJECT_TABCONSTRAINT
@ OBJECT_DOMCONSTRAINT
#define list_make3(x1, x2, x3)
Definition pg_list.h:248
#define list_make2(x1, x2)
Definition pg_list.h:246
char * comment
ObjectType objtype
Node * object

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

Referenced by ATPostAlterTypeParse().

◆ refuseDupeIndexAttach()

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

Definition at line 22047 of file tablecmds.c.

22048{
22050
22051 existingIdx = index_get_partition(partitionTbl,
22052 RelationGetRelid(parentIdx));
22054 ereport(ERROR,
22056 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22058 RelationGetRelationName(parentIdx)),
22059 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
22061 RelationGetRelationName(partitionTbl))));
22062}

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

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

18642{
18649 ListCell *index;
18650
18651 /*
18652 * Check whether relreplident has changed, and update it if so.
18653 */
18658 elog(ERROR, "cache lookup failed for relation \"%s\"",
18661 if (pg_class_form->relreplident != ri_type)
18662 {
18663 pg_class_form->relreplident = ri_type;
18665 }
18668
18669 /*
18670 * Update the per-index indisreplident flags correctly.
18671 */
18673 foreach(index, RelationGetIndexList(rel))
18674 {
18676 bool dirty = false;
18677
18681 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18683
18684 if (thisIndexOid == indexOid)
18685 {
18686 /* Set the bit if not already set. */
18687 if (!pg_index_form->indisreplident)
18688 {
18689 dirty = true;
18690 pg_index_form->indisreplident = true;
18691 }
18692 }
18693 else
18694 {
18695 /* Unset the bit if set. */
18696 if (pg_index_form->indisreplident)
18697 {
18698 dirty = true;
18699 pg_index_form->indisreplident = false;
18700 }
18701 }
18702
18703 if (dirty)
18704 {
18707 InvalidOid, is_internal);
18708
18709 /*
18710 * Invalidate the relcache for the table, so that after we commit
18711 * all sessions will refresh the table's replica identity index
18712 * before attempting any UPDATE or DELETE on the table. (If we
18713 * changed the table's pg_class row above, then a relcache inval
18714 * is already queued due to that; but we might not have.)
18715 */
18717 }
18719 }
18720
18722}
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 15289 of file tablecmds.c.

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

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

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

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

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

Referenced by RememberAllDependentForRebuilding(), and RememberIndexForRebuilding().

◆ RememberIndexForRebuilding()

static void RememberIndexForRebuilding ( Oid  indoid,
AlteredTableInfo tab 
)
static

Definition at line 15602 of file tablecmds.c.

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

References AlteredTableInfo::changedIndexDefs, AlteredTableInfo::changedIndexOids, fb(), get_index_constraint(), lappend(), lappend_oid(), list_member_oid(), OidIsValid, pg_get_indexdef_string(), RememberClusterOnForRebuilding(), RememberConstraintForRebuilding(), and RememberReplicaIdentityForRebuilding().

Referenced by RememberAllDependentForRebuilding().

◆ RememberReplicaIdentityForRebuilding()

static void RememberReplicaIdentityForRebuilding ( Oid  indoid,
AlteredTableInfo tab 
)
static

Definition at line 15515 of file tablecmds.c.

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

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

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

References AlteredTableInfo::changedStatisticsDefs, AlteredTableInfo::changedStatisticsOids, fb(), lappend(), lappend_oid(), list_member_oid(), and pg_get_statisticsobjdef_string().

Referenced by RememberAllDependentForRebuilding().

◆ remove_on_commit_action()

void remove_on_commit_action ( Oid  relid)

Definition at line 19535 of file tablecmds.c.

19536{
19537 ListCell *l;
19538
19539 foreach(l, on_commits)
19540 {
19542
19543 if (oc->relid == relid)
19544 {
19546 break;
19547 }
19548 }
19549}
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 18188 of file tablecmds.c.

18189{
18191 SysScanDesc scan;
18192 ScanKeyData key[3];
18195 AttrMap *attmap;
18196 List *connames;
18197 List *nncolumns;
18198 bool found;
18199 bool is_partitioning;
18200
18202
18207 if (!found)
18208 {
18209 if (is_partitioning)
18210 ereport(ERROR,
18212 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18215 else
18216 ereport(ERROR,
18218 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18221 }
18222
18223 /*
18224 * Search through child columns looking for ones matching parent rel
18225 */
18227 ScanKeyInit(&key[0],
18232 true, NULL, 1, key);
18234 {
18236
18237 /* Ignore if dropped or not inherited */
18238 if (att->attisdropped)
18239 continue;
18240 if (att->attinhcount <= 0)
18241 continue;
18242
18244 NameStr(att->attname)))
18245 {
18246 /* Decrement inhcount and possibly set islocal to true */
18249
18250 copy_att->attinhcount--;
18251 if (copy_att->attinhcount == 0)
18252 copy_att->attislocal = true;
18253
18256 }
18257 }
18258 systable_endscan(scan);
18260
18261 /*
18262 * Likewise, find inherited check and not-null constraints and disinherit
18263 * them. To do this, we first need a list of the names of the parent's
18264 * check constraints. (We cheat a bit by only checking for name matches,
18265 * assuming that the expressions will match.)
18266 *
18267 * For NOT NULL columns, we store column numbers to match, mapping them in
18268 * to the child rel's attribute numbers.
18269 */
18272 false);
18273
18275 ScanKeyInit(&key[0],
18280 true, NULL, 1, key);
18281
18282 connames = NIL;
18283 nncolumns = NIL;
18284
18286 {
18288
18289 if (con->connoinherit)
18290 continue;
18291
18292 if (con->contype == CONSTRAINT_CHECK)
18293 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18294 if (con->contype == CONSTRAINT_NOTNULL)
18295 {
18297
18299 }
18300 }
18301
18302 systable_endscan(scan);
18303
18304 /* Now scan the child's constraints to find matches */
18305 ScanKeyInit(&key[0],
18310 true, NULL, 1, key);
18311
18313 {
18315 bool match = false;
18316
18317 /*
18318 * Match CHECK constraints by name, not-null constraints by column
18319 * number, and ignore all others.
18320 */
18321 if (con->contype == CONSTRAINT_CHECK)
18322 {
18324 {
18325 if (con->contype == CONSTRAINT_CHECK &&
18326 strcmp(NameStr(con->conname), chkname) == 0)
18327 {
18328 match = true;
18330 break;
18331 }
18332 }
18333 }
18334 else if (con->contype == CONSTRAINT_NOTNULL)
18335 {
18337
18339 {
18340 if (prevattno == child_attno)
18341 {
18342 match = true;
18344 break;
18345 }
18346 }
18347 }
18348 else
18349 continue;
18350
18351 if (match)
18352 {
18353 /* Decrement inhcount and possibly set islocal to true */
18356
18357 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18358 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18360
18361 copy_con->coninhcount--;
18362 if (copy_con->coninhcount == 0)
18363 copy_con->conislocal = true;
18364
18367 }
18368 }
18369
18370 /* We should have matched all constraints */
18371 if (connames != NIL || nncolumns != NIL)
18372 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18375
18376 systable_endscan(scan);
18378
18383
18384 /*
18385 * Post alter hook of this inherits. Since object_access_hook doesn't take
18386 * multiple object identifiers, we relay oid of parent relation using
18387 * auxiliary_id argument.
18388 */
18392}
bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending, const char *childname)
bool SearchSysCacheExistsAttName(Oid relid, const char *attname)
Definition syscache.c:518
#define child_dependency_type(child_is_partition)
Definition tablecmds.c: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 11998 of file tablecmds.c.

12000{
12004 SysScanDesc scan;
12006
12007 ScanKeyInit(&key,
12010 ObjectIdGetDatum(conrelid));
12011
12014 true, NULL, 1, &key);
12016 while ((consttup = systable_getnext(scan)) != NULL)
12017 {
12019
12020 if (conform->conparentid != conoid)
12021 continue;
12022 else
12023 {
12024 ObjectAddress addr;
12028
12031
12032 /*
12033 * First we must delete the dependency record that binds the
12034 * constraint records together.
12035 */
12037 conform->oid,
12040 conoid);
12041 Assert(n == 1); /* actually only one is expected */
12042
12043 /*
12044 * Now search for the triggers for this constraint and set them up
12045 * for deletion too
12046 */
12052 true, NULL, 1, &key2);
12053 while ((trigtup = systable_getnext(scan2)) != NULL)
12054 {
12058 }
12060 }
12061 }
12062 /* make the dependency deletions visible */
12066 systable_endscan(scan);
12067}
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:249
long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype, Oid refclassId, Oid refobjectId)
Definition pg_depend.c:400

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

Referenced by AttachPartitionForeignKey().

◆ RemoveRelations()

void RemoveRelations ( DropStmt drop)

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

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

Referenced by ExecRenameStmt().

◆ RenameRelation()

ObjectAddress RenameRelation ( RenameStmt stmt)

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

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

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

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

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

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

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

Referenced by ATExecSplitPartition().

◆ storage_name()

static const char * storage_name ( char  c)
static

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

13575{
13576 ListCell *l;
13577 int attnum;
13578
13579 attnum = 0;
13580 foreach(l, colList)
13581 {
13582 char *attname = strVal(lfirst(l));
13585
13588 ereport(ERROR,
13590 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13591 attname)));
13593 if (attform->attnum < 0)
13594 ereport(ERROR,
13596 errmsg("system columns cannot be used in foreign keys")));
13597 if (attnum >= INDEX_MAX_KEYS)
13598 ereport(ERROR,
13600 errmsg("cannot have more than %d keys in a foreign key",
13601 INDEX_MAX_KEYS)));
13602 attnums[attnum] = attform->attnum;
13603 if (atttypids != NULL)
13604 atttypids[attnum] = attform->atttypid;
13605 if (attcollids != NULL)
13606 attcollids[attnum] = attform->attcollation;
13608 attnum++;
13609 }
13610
13611 return attnum;
13612}

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

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

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

Referenced by ATAddForeignKeyConstraint().

◆ transformFkeyGetPrimaryKey()

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

Definition at line 13628 of file tablecmds.c.

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

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

References AccessShareLock, addNSItemToQuery(), addRangeTableEntryForRelation(), assign_expr_collations(), copyObject, ereport, errcode(), errmsg, ERROR, EXPR_KIND_PARTITION_EXPRESSION, fb(), lappend(), lfirst_node, list_length(), PartitionSpec::location, make_parsestate(), makeNode, NIL, PARTITION_STRATEGY_LIST, PartitionSpec::partParams, PartitionSpec::strategy, and transformExpr().

Referenced by DefineRelation().

◆ truncate_check_activity()

static void truncate_check_activity ( Relation  rel)
static

Definition at line 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:123
#define InvokeObjectTruncateHook(objectId)

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

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

◆ tryAttachPartitionForeignKey()

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

Definition at line 11771 of file tablecmds.c.

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

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

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

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

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

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

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

Referenced by ATRewriteTables().

◆ validatePartitionedIndex()

static void validatePartitionedIndex ( Relation  partedIdx,
Relation  partedTbl 
)
static

Definition at line 22071 of file tablecmds.c.

22072{
22074 SysScanDesc scan;
22076 int tuples = 0;
22078 bool updated = false;
22079
22080 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
22081
22082 /*
22083 * Scan pg_inherits for this parent index. Count each valid index we find
22084 * (verifying the pg_index entry for each), and if we reach the total
22085 * amount we expect, we can mark this parent index as valid.
22086 */
22092 NULL, 1, &key);
22093 while ((inhTup = systable_getnext(scan)) != NULL)
22094 {
22098
22100 ObjectIdGetDatum(inhForm->inhrelid));
22102 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
22104 if (indexForm->indisvalid)
22105 tuples += 1;
22107 }
22108
22109 /* Done with pg_inherits */
22110 systable_endscan(scan);
22112
22113 /*
22114 * If we found as many inherited indexes as the partitioned table has
22115 * partitions, we're good; update pg_index to set indisvalid.
22116 */
22117 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
22118 {
22122
22127 elog(ERROR, "cache lookup failed for index %u",
22130
22131 indexForm->indisvalid = true;
22132 updated = true;
22133
22135
22138 }
22139
22140 /*
22141 * If this index is in turn a partition of a larger index, validating it
22142 * might cause the parent to become valid also. Try that.
22143 */
22144 if (updated && partedIdx->rd_rel->relispartition)
22145 {
22148 Relation parentIdx,
22149 parentTbl;
22150
22151 /* make sure we see the validation we just did */
22153
22158 Assert(!parentIdx->rd_index->indisvalid);
22159
22160 validatePartitionedIndex(parentIdx, parentTbl);
22161
22164 }
22165}

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

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

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

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

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

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits