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

Go to the source code of this file.

Data Structures

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

Macros

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

Typedefs

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

Enumerations

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

Functions

static void truncate_check_rel (Oid relid, Form_pg_class reltuple)
 
static void truncate_check_perms (Oid relid, Form_pg_class reltuple)
 
static void truncate_check_activity (Relation rel)
 
static void RangeVarCallbackForTruncate (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
static ListMergeAttributes (List *columns, const List *supers, char relpersistence, bool is_partition, List **supconstr, List **supnotnulls)
 
static ListMergeCheckConstraint (List *constraints, const char *name, Node *expr, bool is_enforced)
 
static void MergeChildAttribute (List *inh_columns, int exist_attno, int newcol_attno, const ColumnDef *newdef)
 
static ColumnDefMergeInheritedAttribute (List *inh_columns, int exist_attno, const ColumnDef *newdef)
 
static void MergeAttributesIntoExisting (Relation child_rel, Relation parent_rel, bool ispartition)
 
static void MergeConstraintsIntoExisting (Relation child_rel, Relation parent_rel)
 
static void StoreCatalogInheritance (Oid relationId, List *supers, bool child_is_partition)
 
static void StoreCatalogInheritance1 (Oid relationId, Oid parentOid, int32 seqNumber, Relation inhRelation, bool child_is_partition)
 
static int findAttrByName (const char *attributeName, const List *columns)
 
static void AlterIndexNamespaces (Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved)
 
static void AlterSeqNamespaces (Relation classRel, Relation rel, Oid oldNspOid, Oid newNspOid, ObjectAddresses *objsMoved, LOCKMODE lockmode)
 
static ObjectAddress ATExecAlterConstraint (List **wqueue, Relation rel, ATAlterConstraint *cmdcon, bool recurse, LOCKMODE lockmode)
 
static bool ATExecAlterConstraintInternal (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Relation rel, HeapTuple contuple, bool recurse, LOCKMODE lockmode)
 
static bool ATExecAlterFKConstrEnforceability (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Relation tgrel, Oid fkrelid, Oid pkrelid, HeapTuple contuple, LOCKMODE lockmode, Oid ReferencedParentDelTrigger, Oid ReferencedParentUpdTrigger, Oid ReferencingParentInsTrigger, Oid ReferencingParentUpdTrigger)
 
static bool ATExecAlterCheckConstrEnforceability (List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple, bool recurse, bool recursing, List *changing_conids, 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, List *changing_conids, 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 bool ATCheckCheckConstrHasEnforcedParent (Relation conrel, Relation rel, HeapTuple contuple, List *changing_conids, Oid *enforced_parentoid)
 
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 bool constraints_equivalent (HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
 
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 RememberWholeRowDependentForRebuilding (AlteredTableInfo *tab, AlterTableType subtype, Relation rel)
 
static void RememberConstraintForRebuilding (Oid conoid, AlteredTableInfo *tab)
 
static void RememberIndexForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
static void RememberStatisticsForRebuilding (Oid stxoid, AlteredTableInfo *tab)
 
static void ATPostAlterTypeCleanup (List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
 
static void ATPostAlterTypeParse (Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)
 
static void RebuildConstraintComment (AlteredTableInfo *tab, AlterTablePass pass, Oid objid, Relation rel, List *domname, const char *conname)
 
static void TryReuseIndex (Oid oldId, IndexStmt *stmt)
 
static void TryReuseForeignKey (Oid oldId, Constraint *con)
 
static ObjectAddress ATExecAlterColumnGenericOptions (Relation rel, const char *colName, List *options, LOCKMODE lockmode)
 
static void change_owner_fix_column_acls (Oid relationOid, Oid oldOwnerId, Oid newOwnerId)
 
static void change_owner_recurse_to_sequences (Oid relationOid, Oid newOwnerId, LOCKMODE lockmode)
 
static ObjectAddress ATExecClusterOn (Relation rel, const char *indexName, LOCKMODE lockmode)
 
static void ATExecDropCluster (Relation rel, LOCKMODE lockmode)
 
static void ATPrepSetAccessMethod (AlteredTableInfo *tab, Relation rel, const char *amname)
 
static void ATExecSetAccessMethodNoStorage (Relation rel, Oid newAccessMethodId)
 
static void ATPrepChangePersistence (AlteredTableInfo *tab, Relation rel, bool toLogged)
 
static void ATPrepSetTableSpace (AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
 
static void ATExecSetTableSpace (Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
 
static void ATExecSetTableSpaceNoStorage (Relation rel, Oid newTableSpace)
 
static void ATExecSetRelOptions (Relation rel, List *defList, AlterTableType operation, LOCKMODE lockmode)
 
static void ATExecEnableDisableTrigger (Relation rel, const char *trigname, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
 
static void ATExecEnableDisableRule (Relation rel, const char *rulename, char fires_when, LOCKMODE lockmode)
 
static void ATPrepChangeInherit (Relation child_rel)
 
static ObjectAddress ATExecAddInherit (Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
 
static ObjectAddress ATExecDropInherit (Relation rel, RangeVar *parent, LOCKMODE lockmode)
 
static void drop_parent_dependency (Oid relid, Oid refclassid, Oid refobjid, DependencyType deptype)
 
static ObjectAddress ATExecAddOf (Relation rel, const TypeName *ofTypename, LOCKMODE lockmode)
 
static void ATExecDropOf (Relation rel, LOCKMODE lockmode)
 
static void ATExecReplicaIdentity (Relation rel, ReplicaIdentityStmt *stmt, LOCKMODE lockmode)
 
static void ATExecGenericOptions (Relation rel, List *options)
 
static void ATExecSetRowSecurity (Relation rel, bool rls)
 
static void ATExecForceNoForceRowSecurity (Relation rel, bool force_rls)
 
static ObjectAddress ATExecSetCompression (Relation rel, const char *column, Node *newValue, LOCKMODE lockmode)
 
static void index_copy_data (Relation rel, RelFileLocator newrlocator)
 
static const charstorage_name (char c)
 
static void RangeVarCallbackForDropRelation (const RangeVar *rel, Oid relOid, Oid oldRelOid, void *arg)
 
static void RangeVarCallbackForAlterRelation (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
static PartitionSpectransformPartitionSpec (Relation rel, PartitionSpec *partspec)
 
static void ComputePartitionAttrs (ParseState *pstate, Relation rel, List *partParams, AttrNumber *partattrs, List **partexprs, Oid *partopclass, Oid *partcollation, PartitionStrategy strategy)
 
static void CreateInheritance (Relation child_rel, Relation parent_rel, bool ispartition)
 
static void RemoveInheritance (Relation child_rel, Relation parent_rel, bool expect_detached)
 
static ObjectAddress ATExecAttachPartition (List **wqueue, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
 
static void AttachPartitionEnsureIndexes (List **wqueue, Relation rel, Relation attachrel)
 
static void QueuePartitionConstraintValidation (List **wqueue, Relation scanrel, List *partConstraint, bool validate_default)
 
static void CloneRowTriggersToPartition (Relation parent, Relation partition)
 
static void DropClonedTriggersFromPartition (Oid partitionId)
 
static ObjectAddress ATExecDetachPartition (List **wqueue, AlteredTableInfo *tab, Relation rel, RangeVar *name, bool concurrent)
 
static void DetachPartitionFinalize (Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
 
static ObjectAddress ATExecDetachPartitionFinalize (Relation rel, RangeVar *name)
 
static ObjectAddress ATExecAttachPartitionIdx (List **wqueue, Relation parentIdx, RangeVar *name)
 
static void validatePartitionedIndex (Relation partedIdx, Relation partedTbl)
 
static void refuseDupeIndexAttach (Relation parentIdx, Relation partIdx, Relation partitionTbl)
 
static void verifyPartitionIndexNotNull (IndexInfo *iinfo, Relation partition)
 
static ListGetParentedForeignKeyRefs (Relation partition)
 
static void ATDetachCheckNoForeignKeyRefs (Relation partition)
 
static char GetAttributeCompression (Oid atttypid, const char *compression)
 
static char GetAttributeStorage (Oid atttypid, const char *storagemode)
 
static void ATExecMergePartitions (List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
 
static void ATExecSplitPartition (List **wqueue, AlteredTableInfo *tab, Relation rel, PartitionCmd *cmd, AlterTableUtilityContext *context)
 
static ListcollectPartitionIndexExtDeps (List *partitionOids)
 
static void applyPartitionIndexExtDeps (Oid newPartOid, List *extDepState)
 
static void freePartitionIndexExtDeps (List *extDepState)
 
ObjectAddress DefineRelation (CreateStmt *stmt, char relkind, Oid ownerId, ObjectAddress *typaddress, const char *queryString)
 
TupleDesc BuildDescForRelation (const List *columns)
 
static void DropErrorMsgNonExistent (RangeVar *rel, char rightkind, bool missing_ok)
 
static void DropErrorMsgWrongType (const char *relname, char wrongkind, char rightkind)
 
void RemoveRelations (DropStmt *drop)
 
void ExecuteTruncate (TruncateStmt *stmt)
 
void ExecuteTruncateGuts (List *explicit_rels, List *relids, List *relids_logged, DropBehavior behavior, bool restart_seqs, bool run_as_table_owner)
 
void SetRelationHasSubclass (Oid relationId, bool relhassubclass)
 
bool CheckRelationTableSpaceMove (Relation rel, Oid newTableSpaceId)
 
void SetRelationTableSpace (Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
 
static void renameatt_check (Oid myrelid, Form_pg_class classform, bool recursing)
 
static AttrNumber renameatt_internal (Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
 
static void RangeVarCallbackForRenameAttribute (const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
 
ObjectAddress renameatt (RenameStmt *stmt)
 
static ObjectAddress rename_constraint_internal (Oid myrelid, Oid mytypid, const char *oldconname, const char *newconname, bool recurse, bool recursing, int expected_parents)
 
ObjectAddress RenameConstraint (RenameStmt *stmt)
 
ObjectAddress RenameRelation (RenameStmt *stmt)
 
void RenameRelationInternal (Oid myrelid, const char *newrelname, bool is_internal, bool is_index)
 
void ResetRelRewrite (Oid myrelid)
 
void CheckTableNotInUse (Relation rel, const char *stmt)
 
Oid AlterTableLookupRelation (AlterTableStmt *stmt, LOCKMODE lockmode)
 
void AlterTable (AlterTableStmt *stmt, LOCKMODE lockmode, AlterTableUtilityContext *context)
 
void AlterTableInternal (Oid relid, List *cmds, bool recurse)
 
LOCKMODE AlterTableGetLockLevel (List *cmds)
 
static const charalter_table_type_to_string (AlterTableType cmdtype)
 
void find_composite_type_dependencies (Oid typeOid, Relation origRelation, const char *origTypeName)
 
void check_of_type (HeapTuple typetuple)
 
static void SetIndexStorageProperties (Relation rel, Relation attrelation, AttrNumber attnum, bool setstorage, char newstorage, bool setcompression, char newcompression, LOCKMODE lockmode)
 
static Oid CreateFKCheckTrigger (Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentTrigOid, bool on_insert)
 
static void RememberReplicaIdentityForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
static void RememberClusterOnForRebuilding (Oid indoid, AlteredTableInfo *tab)
 
void ATExecChangeOwner (Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lockmode)
 
Oid AlterTableMoveAll (AlterTableMoveAllStmt *stmt)
 
static chardecompile_conbin (HeapTuple contup, TupleDesc tupdesc)
 
static void MarkInheritDetached (Relation child_rel, Relation parent_rel)
 
static void relation_mark_replica_identity (Relation rel, char ri_type, Oid indexOid, bool is_internal)
 
ObjectAddress AlterTableNamespace (AlterObjectSchemaStmt *stmt, Oid *oldschema)
 
void AlterTableNamespaceInternal (Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)
 
void AlterRelationNamespaceInternal (Relation classRel, Oid relOid, Oid oldNspOid, Oid newNspOid, bool hasDependEntry, ObjectAddresses *objsMoved)
 
void register_on_commit_action (Oid relid, OnCommitAction action)
 
void remove_on_commit_action (Oid relid)
 
void PreCommit_on_commit_actions (void)
 
void AtEOXact_on_commit_actions (bool isCommit)
 
void AtEOSubXact_on_commit_actions (bool isCommit, SubTransactionId mySubid, SubTransactionId parentSubid)
 
void RangeVarCallbackMaintainsTable (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
void RangeVarCallbackOwnsRelation (const RangeVar *relation, Oid relId, Oid oldRelId, void *arg)
 
bool PartConstraintImpliedByRelConstraint (Relation scanrel, List *partConstraint)
 
static void attachPartitionTable (List **wqueue, Relation rel, Relation attachrel, PartitionBoundSpec *bound)
 
static void RangeVarCallbackForAttachIndex (const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
 
static void buildExpressionExecutionStates (AlteredTableInfo *tab, Relation newPartRel, EState *estate)
 
static void evaluateGeneratedExpressionsAndCheckConstraints (AlteredTableInfo *tab, Relation newPartRel, TupleTableSlot *insertslot, ExprContext *econtext)
 
static ListgetAttributesList (Relation parent_rel)
 
static void createTableConstraints (List **wqueue, AlteredTableInfo *tab, Relation parent_rel, Relation newRel)
 
static Relation createPartitionTable (List **wqueue, RangeVar *newPartName, Relation parent_rel, Oid ownerId)
 
static void MergePartitionsMoveRows (List **wqueue, List *mergingPartitions, Relation newPartRel)
 
static void detachPartitionTable (Relation parent_rel, Relation child_rel, Oid defaultPartOid)
 
static bool equal_oid_lists (const List *a, const List *b)
 
static int cmp_partition_index_ext_dep (const ListCell *a, const ListCell *b)
 
static SplitPartitionContextcreateSplitPartitionContext (Relation partRel)
 
static void deleteSplitPartitionContext (SplitPartitionContext *pc, List **wqueue, uint32 ti_options)
 
static void SplitPartitionMoveRows (List **wqueue, Relation rel, Relation splitRel, List *partlist, List *newPartRels)
 

Variables

static Liston_commits = NIL
 
static const struct dropmsgstrings dropmsgstringarray []
 

Macro Definition Documentation

◆ AT_NUM_PASSES

#define AT_NUM_PASSES   (AT_PASS_MISC + 1)

Definition at line 170 of file tablecmds.c.

◆ ATT_COMPOSITE_TYPE

#define ATT_COMPOSITE_TYPE   0x0010

Definition at line 342 of file tablecmds.c.

◆ ATT_FOREIGN_TABLE

#define ATT_FOREIGN_TABLE   0x0020

Definition at line 343 of file tablecmds.c.

◆ ATT_INDEX

#define ATT_INDEX   0x0008

Definition at line 341 of file tablecmds.c.

◆ ATT_MATVIEW

#define ATT_MATVIEW   0x0004

Definition at line 340 of file tablecmds.c.

◆ ATT_PARTITIONED_INDEX

#define ATT_PARTITIONED_INDEX   0x0040

Definition at line 344 of file tablecmds.c.

◆ ATT_PARTITIONED_TABLE

#define ATT_PARTITIONED_TABLE   0x0100

Definition at line 346 of file tablecmds.c.

◆ ATT_SEQUENCE

#define ATT_SEQUENCE   0x0080

Definition at line 345 of file tablecmds.c.

◆ ATT_TABLE

#define ATT_TABLE   0x0001

Definition at line 338 of file tablecmds.c.

◆ ATT_VIEW

#define ATT_VIEW   0x0002

Definition at line 339 of file tablecmds.c.

◆ child_dependency_type

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

Definition at line 396 of file tablecmds.c.

@ DEPENDENCY_NORMAL
Definition dependency.h:33

Typedef Documentation

◆ addFkConstraintSides

◆ AlteredTableInfo

◆ AlterTablePass

◆ ForeignTruncateInfo

◆ NewColumnValue

◆ NewConstraint

◆ OnCommitItem

◆ PartitionIndexExtDepEntry

◆ SplitPartitionContext

Enumeration Type Documentation

◆ addFkConstraintSides

Enumerator
addFkReferencedSide 
addFkReferencingSide 
addFkBothSides 

Definition at line 363 of file tablecmds.c.

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

◆ AlterTablePass

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

Definition at line 152 of file tablecmds.c.

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

Function Documentation

◆ add_column_collation_dependency()

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

Definition at line 7858 of file tablecmds.c.

7859{
7861 referenced;
7862
7863 /* We know the default collation is pinned, so don't bother recording it */
7865 {
7867 myself.objectId = relid;
7868 myself.objectSubId = attnum;
7870 referenced.objectId = collid;
7871 referenced.objectSubId = 0;
7873 }
7874}
#define OidIsValid(objectId)
Definition c.h:917
Oid collid
int16 attnum
void recordDependencyOn(const ObjectAddress *depender, const ObjectAddress *referenced, DependencyType behavior)
Definition pg_depend.c:51
static int fb(int x)

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

Referenced by ATExecAddColumn(), and ATExecAlterColumnType().

◆ add_column_datatype_dependency()

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

Definition at line 7840 of file tablecmds.c.

7841{
7843 referenced;
7844
7846 myself.objectId = relid;
7847 myself.objectSubId = attnum;
7848 referenced.classId = TypeRelationId;
7849 referenced.objectId = typid;
7850 referenced.objectSubId = 0;
7852}

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

10877{
10878 ObjectAddress address;
10879 Oid constrOid;
10880 char *conname;
10881 bool conislocal;
10883 bool connoinherit;
10884
10885 /*
10886 * Verify relkind for each referenced partition. At the top level, this
10887 * is redundant with a previous check, but we need it when recursing.
10888 */
10889 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10890 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10891 ereport(ERROR,
10893 errmsg("referenced relation \"%s\" is not a table",
10895
10896 /*
10897 * Caller supplies us with a constraint name; however, it may be used in
10898 * this partition, so come up with a different one in that case. Unless
10899 * truncation to NAMEDATALEN dictates otherwise, the new name will be the
10900 * supplied name with an underscore and digit(s) appended.
10901 */
10903 RelationGetRelid(rel),
10904 constraintname))
10905 conname = ChooseConstraintName(constraintname,
10906 NULL,
10907 "",
10909 else
10910 conname = constraintname;
10911
10912 if (fkconstraint->conname == NULL)
10913 fkconstraint->conname = pstrdup(conname);
10914
10916 {
10917 conislocal = false;
10918 coninhcount = 1;
10919 connoinherit = false;
10920 }
10921 else
10922 {
10923 conislocal = true;
10924 coninhcount = 0;
10925
10926 /*
10927 * always inherit for partitioned tables, never for legacy inheritance
10928 */
10930 }
10931
10932 /*
10933 * Record the FK constraint in pg_constraint.
10934 */
10938 fkconstraint->deferrable,
10939 fkconstraint->initdeferred,
10940 fkconstraint->is_enforced,
10941 fkconstraint->initially_valid,
10943 RelationGetRelid(rel),
10944 fkattnum,
10945 numfks,
10946 numfks,
10947 InvalidOid, /* not a domain constraint */
10948 indexOid,
10950 pkattnum,
10954 numfks,
10955 fkconstraint->fk_upd_action,
10956 fkconstraint->fk_del_action,
10959 fkconstraint->fk_matchtype,
10960 NULL, /* no exclusion constraint */
10961 NULL, /* no check constraint */
10962 NULL,
10963 conislocal, /* islocal */
10964 coninhcount, /* inhcount */
10965 connoinherit, /* conNoInherit */
10966 with_period, /* conPeriod */
10967 is_internal); /* is_internal */
10968
10970
10971 /*
10972 * In partitioning cases, create the dependency entries for this
10973 * constraint. (For non-partitioned cases, relevant entries were created
10974 * by CreateConstraintEntry.)
10975 *
10976 * On the referenced side, we need the constraint to have an internal
10977 * dependency on its parent constraint; this means that this constraint
10978 * cannot be dropped on its own -- only through the parent constraint. It
10979 * also means the containing partition cannot be dropped on its own, but
10980 * it can be detached, at which point this dependency is removed (after
10981 * verifying that no rows are referenced via this FK.)
10982 *
10983 * When processing the referencing side, we link the constraint via the
10984 * special partitioning dependencies: the parent constraint is the primary
10985 * dependent, and the partition on which the foreign key exists is the
10986 * secondary dependency. That way, this constraint is dropped if either
10987 * of these objects is.
10988 *
10989 * Note that this is only necessary for the subsidiary pg_constraint rows
10990 * in partitions; the topmost row doesn't need any of this.
10991 */
10993 {
10995
10997
11001 else
11002 {
11006 }
11007 }
11008
11009 /* make new constraint visible, in case we add more */
11011
11012 return address;
11013}
#define Assert(condition)
Definition c.h:1002
int16_t int16
Definition c.h:678
@ DEPENDENCY_INTERNAL
Definition dependency.h:35
@ DEPENDENCY_PARTITION_PRI
Definition dependency.h:36
@ DEPENDENCY_PARTITION_SEC
Definition dependency.h:37
int errcode(int sqlerrcode)
Definition elog.c:875
#define ERROR
Definition elog.h:40
#define ereport(elevel,...)
Definition elog.h:152
char * pstrdup(const char *in)
Definition mcxt.c:1910
static char * errmsg
#define ObjectAddressSet(addr, class_id, object_id)
Oid CreateConstraintEntry(const char *constraintName, Oid constraintNamespace, char constraintType, bool isDeferrable, bool isDeferred, bool isEnforced, bool isValidated, Oid parentConstrId, Oid relId, const int16 *constraintKey, int constraintNKeys, int constraintNTotalKeys, Oid domainId, Oid indexRelId, Oid foreignRelId, const int16 *foreignKey, const Oid *pfEqOp, const Oid *ppEqOp, const Oid *ffEqOp, int foreignNKeys, char foreignUpdateType, char foreignDeleteType, const int16 *fkDeleteSetCols, int numFkDeleteSetCols, char foreignMatchType, const Oid *exclOp, Node *conExpr, const char *conBin, bool conIsLocal, int16 conInhCount, bool conNoInherit, bool conPeriod, bool is_internal)
bool ConstraintNameIsUsed(ConstraintCategory conCat, Oid objId, const char *conname)
char * ChooseConstraintName(const char *name1, const char *name2, const char *label, Oid namespaceid, List *others)
@ CONSTRAINT_RELATION
#define NIL
Definition pg_list.h:68
#define InvalidOid
unsigned int Oid
#define RelationGetRelid(relation)
Definition rel.h:516
#define RelationGetRelationName(relation)
Definition rel.h:550
#define RelationGetNamespace(relation)
Definition rel.h:557
Form_pg_class rd_rel
Definition rel.h:111
void CommandCounterIncrement(void)
Definition xact.c:1130

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

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

◆ addFkRecurseReferenced()

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

Definition at line 11048 of file tablecmds.c.

11057{
11060
11063
11064 /*
11065 * Create action triggers to enforce the constraint, or skip them if the
11066 * constraint is NOT ENFORCED.
11067 */
11068 if (fkconstraint->is_enforced)
11072 parentConstr, indexOid,
11075
11076 /*
11077 * If the referenced table is partitioned, recurse on ourselves to handle
11078 * each partition. We need one pg_constraint row created for each
11079 * partition in addition to the pg_constraint row for the parent table.
11080 */
11081 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11082 {
11084
11085 for (int i = 0; i < pd->nparts; i++)
11086 {
11087 Relation partRel;
11088 AttrMap *map;
11091 ObjectAddress address;
11092
11093 /* XXX would it be better to acquire these locks beforehand? */
11094 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
11095
11096 /*
11097 * Map the attribute numbers in the referenced side of the FK
11098 * definition to match the partition's column layout.
11099 */
11102 false);
11103 if (map)
11104 {
11106 for (int j = 0; j < numfks; j++)
11107 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
11108 }
11109 else
11111
11112 /* Determine the index to use at this level */
11113 partIndexId = index_get_partition(partRel, indexOid);
11114 if (!OidIsValid(partIndexId))
11115 elog(ERROR, "index for %u not found in partition %s",
11116 indexOid, RelationGetRelationName(partRel));
11117
11118 /* Create entry at this level ... */
11120 fkconstraint->conname, fkconstraint, rel,
11121 partRel, partIndexId, parentConstr,
11125 fkdelsetcols, true, with_period);
11126 /* ... and recurse to our children */
11128 partIndexId, address.objectId, numfks,
11134 with_period);
11135
11136 /* Done -- clean up (but keep the lock) */
11137 table_close(partRel, NoLock);
11138 if (map)
11139 {
11141 free_attrmap(map);
11142 }
11143 }
11144 }
11145}
void free_attrmap(AttrMap *map)
Definition attmap.c:56
AttrMap * build_attrmap_by_name_if_req(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition attmap.c:261
int16 AttrNumber
Definition attnum.h:21
#define elog(elevel,...)
Definition elog.h:228
#define palloc_array(type, count)
Definition fe_memutils.h:91
int j
Definition isn.c:78
int i
Definition isn.c:77
bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode, bool orstronger)
Definition lmgr.c:334
#define NoLock
Definition lockdefs.h:34
#define ShareRowExclusiveLock
Definition lockdefs.h:41
void pfree(void *pointer)
Definition mcxt.c:1619
PartitionDesc RelationGetPartitionDesc(Relation rel, bool omit_detached)
Definition partdesc.c:71
Oid index_get_partition(Relation partition, Oid indexId)
Definition partition.c:176
#define RelationGetDescr(relation)
Definition rel.h:542
AttrNumber * attnums
Definition attmap.h:36
void table_close(Relation relation, LOCKMODE lockmode)
Definition table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition table.c:40
static void createForeignKeyActionTriggers(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, Oid parentDelTrigger, Oid parentUpdTrigger, Oid *deleteTrigOid, Oid *updateTrigOid)
static ObjectAddress addFkConstraint(addFkConstraintSides fkside, char *constraintname, Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool is_internal, bool with_period)
static void addFkRecurseReferenced(Constraint *fkconstraint, Relation rel, Relation pkrel, Oid indexOid, Oid parentConstr, int numfks, int16 *pkattnum, int16 *fkattnum, Oid *pfeqoperators, Oid *ppeqoperators, Oid *ffeqoperators, int numfkdelsetcols, int16 *fkdelsetcols, bool old_check_ok, Oid parentDelTrigger, Oid parentUpdTrigger, bool with_period)

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

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

◆ addFkRecurseReferencing()

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

Definition at line 11186 of file tablecmds.c.

11194{
11197
11201
11202 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11203 ereport(ERROR,
11205 errmsg("foreign key constraints are not supported on foreign tables")));
11206
11207 /*
11208 * Add check triggers if the constraint is ENFORCED, and if needed,
11209 * schedule them to be checked in Phase 3.
11210 *
11211 * If the relation is partitioned, drill down to do it to its partitions.
11212 */
11213 if (fkconstraint->is_enforced)
11218 indexOid,
11221
11222 if (rel->rd_rel->relkind == RELKIND_RELATION)
11223 {
11224 /*
11225 * Tell Phase 3 to check that the constraint is satisfied by existing
11226 * rows. We can skip this during table creation, when constraint is
11227 * specified as NOT ENFORCED, or when requested explicitly by
11228 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11229 * recreating a constraint following a SET DATA TYPE operation that
11230 * did not impugn its validity.
11231 */
11232 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11233 fkconstraint->is_enforced)
11234 {
11236 AlteredTableInfo *tab;
11237
11238 tab = ATGetQueueEntry(wqueue, rel);
11239
11242 newcon->contype = CONSTR_FOREIGN;
11243 newcon->refrelid = RelationGetRelid(pkrel);
11244 newcon->refindid = indexOid;
11245 newcon->conid = parentConstr;
11246 newcon->conwithperiod = fkconstraint->fk_with_period;
11247 newcon->qual = (Node *) fkconstraint;
11248
11249 tab->constraints = lappend(tab->constraints, newcon);
11250 }
11251 }
11252 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11253 {
11256
11257 /*
11258 * Triggers of the foreign keys will be manipulated a bunch of times
11259 * in the loop below. To avoid repeatedly opening/closing the trigger
11260 * catalog relation, we open it here and pass it to the subroutines
11261 * called below.
11262 */
11264
11265 /*
11266 * Recurse to take appropriate action on each partition; either we
11267 * find an existing constraint to reparent to ours, or we create a new
11268 * one.
11269 */
11270 for (int i = 0; i < pd->nparts; i++)
11271 {
11272 Relation partition = table_open(pd->oids[i], lockmode);
11273 List *partFKs;
11274 AttrMap *attmap;
11276 bool attached;
11277 ObjectAddress address;
11278
11280
11282 RelationGetDescr(rel),
11283 false);
11284 for (int j = 0; j < numfks; j++)
11285 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11286
11287 /* Check whether an existing constraint can be repurposed */
11289 attached = false;
11291 {
11293 fk,
11294 partition,
11296 numfks,
11298 pkattnum,
11302 trigrel))
11303 {
11304 attached = true;
11305 break;
11306 }
11307 }
11308 if (attached)
11309 {
11311 continue;
11312 }
11313
11314 /*
11315 * No luck finding a good constraint to reuse; create our own.
11316 */
11318 fkconstraint->conname, fkconstraint,
11319 partition, pkrel, indexOid, parentConstr,
11324 with_period);
11325
11326 /* call ourselves to finalize the creation and we're done */
11328 indexOid,
11329 address.objectId,
11330 numfks,
11331 pkattnum,
11339 lockmode,
11342 with_period);
11343
11345 }
11346
11348 }
11349}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition attmap.c:175
#define palloc0_object(type)
Definition fe_memutils.h:90
List * lappend(List *list, void *datum)
Definition list.c:339
#define RowExclusiveLock
Definition lockdefs.h:38
char * get_constraint_name(Oid conoid)
Definition lsyscache.c:1307
#define copyObject(obj)
Definition nodes.h:230
@ CONSTR_FOREIGN
#define INDEX_MAX_KEYS
#define foreach_node(type, var, lst)
Definition pg_list.h:528
List * RelationGetFKeyList(Relation relation)
Definition relcache.c:4743
Definition pg_list.h:54
Definition nodes.h:133
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition tablecmds.c:6683
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:4541
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 6717 of file tablecmds.c.

6718{
6719 switch (cmdtype)
6720 {
6721 case AT_AddColumn:
6722 case AT_AddColumnToView:
6723 return "ADD COLUMN";
6724 case AT_ColumnDefault:
6726 return "ALTER COLUMN ... SET DEFAULT";
6727 case AT_DropNotNull:
6728 return "ALTER COLUMN ... DROP NOT NULL";
6729 case AT_SetNotNull:
6730 return "ALTER COLUMN ... SET NOT NULL";
6731 case AT_SetExpression:
6732 return "ALTER COLUMN ... SET EXPRESSION";
6733 case AT_DropExpression:
6734 return "ALTER COLUMN ... DROP EXPRESSION";
6735 case AT_SetStatistics:
6736 return "ALTER COLUMN ... SET STATISTICS";
6737 case AT_SetOptions:
6738 return "ALTER COLUMN ... SET";
6739 case AT_ResetOptions:
6740 return "ALTER COLUMN ... RESET";
6741 case AT_SetStorage:
6742 return "ALTER COLUMN ... SET STORAGE";
6743 case AT_SetCompression:
6744 return "ALTER COLUMN ... SET COMPRESSION";
6745 case AT_DropColumn:
6746 return "DROP COLUMN";
6747 case AT_AddIndex:
6748 case AT_ReAddIndex:
6749 return NULL; /* not real grammar */
6750 case AT_AddConstraint:
6751 case AT_ReAddConstraint:
6754 return "ADD CONSTRAINT";
6755 case AT_AlterConstraint:
6756 return "ALTER CONSTRAINT";
6758 return "VALIDATE CONSTRAINT";
6759 case AT_DropConstraint:
6760 return "DROP CONSTRAINT";
6761 case AT_ReAddComment:
6762 return NULL; /* not real grammar */
6763 case AT_AlterColumnType:
6764 return "ALTER COLUMN ... SET DATA TYPE";
6766 return "ALTER COLUMN ... OPTIONS";
6767 case AT_ChangeOwner:
6768 return "OWNER TO";
6769 case AT_ClusterOn:
6770 return "CLUSTER ON";
6771 case AT_DropCluster:
6772 return "SET WITHOUT CLUSTER";
6773 case AT_SetAccessMethod:
6774 return "SET ACCESS METHOD";
6775 case AT_SetLogged:
6776 return "SET LOGGED";
6777 case AT_SetUnLogged:
6778 return "SET UNLOGGED";
6779 case AT_DropOids:
6780 return "SET WITHOUT OIDS";
6781 case AT_SetTableSpace:
6782 return "SET TABLESPACE";
6783 case AT_SetRelOptions:
6784 return "SET";
6785 case AT_ResetRelOptions:
6786 return "RESET";
6788 return NULL; /* not real grammar */
6789 case AT_EnableTrig:
6790 return "ENABLE TRIGGER";
6792 return "ENABLE ALWAYS TRIGGER";
6794 return "ENABLE REPLICA TRIGGER";
6795 case AT_DisableTrig:
6796 return "DISABLE TRIGGER";
6797 case AT_EnableTrigAll:
6798 return "ENABLE TRIGGER ALL";
6799 case AT_DisableTrigAll:
6800 return "DISABLE TRIGGER ALL";
6801 case AT_EnableTrigUser:
6802 return "ENABLE TRIGGER USER";
6803 case AT_DisableTrigUser:
6804 return "DISABLE TRIGGER USER";
6805 case AT_EnableRule:
6806 return "ENABLE RULE";
6808 return "ENABLE ALWAYS RULE";
6810 return "ENABLE REPLICA RULE";
6811 case AT_DisableRule:
6812 return "DISABLE RULE";
6813 case AT_AddInherit:
6814 return "INHERIT";
6815 case AT_DropInherit:
6816 return "NO INHERIT";
6817 case AT_AddOf:
6818 return "OF";
6819 case AT_DropOf:
6820 return "NOT OF";
6821 case AT_ReplicaIdentity:
6822 return "REPLICA IDENTITY";
6824 return "ENABLE ROW SECURITY";
6826 return "DISABLE ROW SECURITY";
6828 return "FORCE ROW SECURITY";
6830 return "NO FORCE ROW SECURITY";
6831 case AT_GenericOptions:
6832 return "OPTIONS";
6833 case AT_AttachPartition:
6834 return "ATTACH PARTITION";
6835 case AT_DetachPartition:
6836 return "DETACH PARTITION";
6838 return "DETACH PARTITION ... FINALIZE";
6839 case AT_MergePartitions:
6840 return "MERGE PARTITIONS";
6841 case AT_SplitPartition:
6842 return "SPLIT PARTITION";
6843 case AT_AddIdentity:
6844 return "ALTER COLUMN ... ADD IDENTITY";
6845 case AT_SetIdentity:
6846 return "ALTER COLUMN ... SET";
6847 case AT_DropIdentity:
6848 return "ALTER COLUMN ... DROP IDENTITY";
6849 case AT_ReAddStatistics:
6850 return NULL; /* not real grammar */
6851 }
6852
6853 return NULL;
6854}
@ 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,
List changing_conids,
LOCKMODE  lockmode 
)
static

Definition at line 12916 of file tablecmds.c.

12921{
12924 ScanKeyData skey[3];
12925
12926 ScanKeyInit(&skey[0],
12929 ObjectIdGetDatum(conrelid));
12930 ScanKeyInit(&skey[1],
12934 ScanKeyInit(&skey[2],
12937 CStringGetDatum(cmdcon->conname));
12938
12940 NULL, 3, skey);
12941
12943 ereport(ERROR,
12945 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12946 cmdcon->conname, get_rel_name(conrelid)));
12947
12949 recurse, recursing, changing_conids,
12950 lockmode);
12951
12953}
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
#define HeapTupleIsValid(tuple)
Definition htup.h:78
char * get_rel_name(Oid relid)
Definition lsyscache.c:2242
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
static Datum CStringGetDatum(const char *X)
Definition postgres.h:383
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
#define BTEqualStrategyNumber
Definition stratnum.h:31
static bool ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple, bool recurse, bool recursing, List *changing_conids, 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 13346 of file tablecmds.c.

13350{
13352 Oid conoid;
13353 ScanKeyData pkey;
13356
13358 conoid = currcon->oid;
13359
13360 ScanKeyInit(&pkey,
13363 ObjectIdGetDatum(conoid));
13364
13366 true, NULL, 1, &pkey);
13367
13369 {
13372
13373 childrel = table_open(childcon->conrelid, lockmode);
13374
13376 childtup, recurse, otherrelids, lockmode);
13378 }
13379
13381}
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 13228 of file tablecmds.c.

13231{
13235
13239 ObjectIdGetDatum(conoid));
13241 NULL, 1, &tgkey);
13243 {
13247
13248 /*
13249 * Remember OIDs of other relation(s) involved in FK constraint.
13250 * (Note: it's likely that we could skip forcing a relcache inval for
13251 * other rels that don't have a trigger whose properties change, but
13252 * let's be conservative.)
13253 */
13254 if (tgform->tgrelid != RelationGetRelid(rel))
13256 tgform->tgrelid);
13257
13258 /*
13259 * Update enable status and deferrability of RI_FKey_noaction_del,
13260 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
13261 * triggers, but not others; see createForeignKeyActionTriggers and
13262 * CreateFKCheckTrigger.
13263 */
13264 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
13265 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
13266 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
13267 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
13268 continue;
13269
13272
13273 copy_tg->tgdeferrable = deferrable;
13274 copy_tg->tginitdeferred = initdeferred;
13276
13278
13280 }
13281
13283}
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 13388 of file tablecmds.c.

13390{
13393
13394 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
13395 cmdcon->alterInheritability);
13396
13399
13400 if (cmdcon->alterEnforceability)
13401 {
13402 copy_con->conenforced = cmdcon->is_enforced;
13403
13404 /*
13405 * NB: The convalidated status is irrelevant when the constraint is
13406 * set to NOT ENFORCED, but for consistency, it should still be set
13407 * appropriately. Similarly, if the constraint is later changed to
13408 * ENFORCED, validation will be performed during phase 3, so it makes
13409 * sense to mark it as valid in that case.
13410 */
13411 copy_con->convalidated = cmdcon->is_enforced;
13412 }
13413 if (cmdcon->alterDeferrability)
13414 {
13415 copy_con->condeferrable = cmdcon->deferrable;
13416 copy_con->condeferred = cmdcon->initdeferred;
13417 }
13418 if (cmdcon->alterInheritability)
13419 copy_con->connoinherit = cmdcon->noinherit;
13420
13423
13424 /* Make new constraint flags visible to others */
13426
13428}
void CacheInvalidateRelcacheByRelid(Oid relid)
Definition inval.c:1688

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

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

◆ AlterFKConstrEnforceabilityRecurse()

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

Definition at line 13297 of file tablecmds.c.

13305{
13307 Oid conoid;
13308 ScanKeyData pkey;
13311
13313 conoid = currcon->oid;
13314
13315 ScanKeyInit(&pkey,
13318 ObjectIdGetDatum(conoid));
13319
13321 true, NULL, 1, &pkey);
13322
13325 pkrelid, childtup, lockmode,
13330
13332}
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 19834 of file tablecmds.c.

19836{
19837 List *indexList;
19838 ListCell *l;
19839
19841
19842 foreach(l, indexList)
19843 {
19844 Oid indexOid = lfirst_oid(l);
19846
19848 thisobj.objectId = indexOid;
19849 thisobj.objectSubId = 0;
19850
19851 /*
19852 * Note: currently, the index will not have its own dependency on the
19853 * namespace, so we don't need to do changeDependencyFor(). There's no
19854 * row type in pg_type, either.
19855 *
19856 * XXX this objsMoved test may be pointless -- surely we have a single
19857 * dependency link from a relation to each index?
19858 */
19860 {
19863 false, objsMoved);
19865 }
19866 }
19867
19869}
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:4848
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 19757 of file tablecmds.c.

19761{
19765 bool already_done = false;
19766
19767 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19770 elog(ERROR, "cache lookup failed for relation %u", relOid);
19772
19773 Assert(classForm->relnamespace == oldNspOid);
19774
19775 thisobj.classId = RelationRelationId;
19776 thisobj.objectId = relOid;
19777 thisobj.objectSubId = 0;
19778
19779 /*
19780 * If the object has already been moved, don't move it again. If it's
19781 * already in the right place, don't move it, but still fire the object
19782 * access hook.
19783 */
19785 if (!already_done && oldNspOid != newNspOid)
19786 {
19787 ItemPointerData otid = classTup->t_self;
19788
19789 /* check for duplicate name (more friendly than unique-index failure) */
19790 if (get_relname_relid(NameStr(classForm->relname),
19792 ereport(ERROR,
19794 errmsg("relation \"%s\" already exists in schema \"%s\"",
19795 NameStr(classForm->relname),
19797
19798 /* classTup is a copy, so OK to scribble on */
19799 classForm->relnamespace = newNspOid;
19800
19803
19804
19805 /* Update dependency on schema if caller said so */
19806 if (hasDependEntry &&
19808 relOid,
19810 oldNspOid,
19811 newNspOid) != 1)
19812 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19813 NameStr(classForm->relname));
19814 }
19815 else
19817 if (!already_done)
19818 {
19820
19822 }
19823
19825}
#define NameStr(name)
Definition c.h:894
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:3682
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition lsyscache.c:2199
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition pg_depend.c:470
HeapTuple SearchSysCacheLockedCopy1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:400

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

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

◆ AlterSeqNamespaces()

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

Definition at line 19879 of file tablecmds.c.

19882{
19884 SysScanDesc scan;
19885 ScanKeyData key[2];
19886 HeapTuple tup;
19887
19888 /*
19889 * SERIAL sequences are those having an auto dependency on one of the
19890 * table's columns (we don't care *which* column, exactly).
19891 */
19893
19894 ScanKeyInit(&key[0],
19898 ScanKeyInit(&key[1],
19902 /* we leave refobjsubid unspecified */
19903
19905 NULL, 2, key);
19906
19907 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19908 {
19911
19912 /* skip dependencies other than auto dependencies on columns */
19913 if (depForm->refobjsubid == 0 ||
19914 depForm->classid != RelationRelationId ||
19915 depForm->objsubid != 0 ||
19916 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19917 continue;
19918
19919 /* Use relation_open just in case it's an index */
19920 seqRel = relation_open(depForm->objid, lockmode);
19921
19922 /* skip non-sequence relations */
19923 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19924 {
19925 /* No need to keep the lock */
19926 relation_close(seqRel, lockmode);
19927 continue;
19928 }
19929
19930 /* Fix the pg_class and pg_depend entries */
19933 true, objsMoved);
19934
19935 /*
19936 * Sequences used to have entries in pg_type, but no longer do. If we
19937 * ever re-instate that, we'll need to move the pg_type entry to the
19938 * new namespace, too (using AlterTypeNamespaceInternal).
19939 */
19941
19942 /* Now we can close it. Keep the lock till end of transaction. */
19944 }
19945
19946 systable_endscan(scan);
19947
19949}
@ 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 4626 of file tablecmds.c.

4628{
4629 Relation rel;
4630
4631 /* Caller is required to provide an adequate lock. */
4632 rel = relation_open(context->relid, NoLock);
4633
4635
4636 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4637}
#define stmt
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:4967

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4700 of file tablecmds.c.

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

4656{
4657 Relation rel;
4658 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4659
4660 rel = relation_open(relid, lockmode);
4661
4663
4664 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4665}
void EventTriggerAlterTableRelid(Oid objectId)
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition tablecmds.c:4700

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4567 of file tablecmds.c.

4568{
4569 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4570 stmt->missing_ok ? RVR_MISSING_OK : 0,
4572 stmt);
4573}
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 17691 of file tablecmds.c.

17692{
17693 List *relations = NIL;
17694 ListCell *l;
17695 ScanKeyData key[1];
17696 Relation rel;
17697 TableScanDesc scan;
17698 HeapTuple tuple;
17701 List *role_oids = roleSpecsToIds(stmt->roles);
17702
17703 /* Ensure we were not asked to move something we can't */
17704 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17705 stmt->objtype != OBJECT_MATVIEW)
17706 ereport(ERROR,
17708 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17709
17710 /* Get the orig and new tablespace OIDs */
17711 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17712 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17713
17714 /* Can't move shared relations in to or out of pg_global */
17715 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17718 ereport(ERROR,
17720 errmsg("cannot move relations in to or out of pg_global tablespace")));
17721
17722 /*
17723 * Must have CREATE rights on the new tablespace, unless it is the
17724 * database default tablespace (which all users implicitly have CREATE
17725 * rights on).
17726 */
17728 {
17730
17732 ACL_CREATE);
17733 if (aclresult != ACLCHECK_OK)
17736 }
17737
17738 /*
17739 * Now that the checks are done, check if we should set either to
17740 * InvalidOid because it is our database's default tablespace.
17741 */
17744
17747
17748 /* no-op */
17750 return new_tablespaceoid;
17751
17752 /*
17753 * Walk the list of objects in the tablespace and move them. This will
17754 * only find objects in our database, of course.
17755 */
17756 ScanKeyInit(&key[0],
17760
17762 scan = table_beginscan_catalog(rel, 1, key);
17763 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17764 {
17766 Oid relOid = relForm->oid;
17767
17768 /*
17769 * Do not move objects in pg_catalog as part of this, if an admin
17770 * really wishes to do so, they can issue the individual ALTER
17771 * commands directly.
17772 *
17773 * Also, explicitly avoid any shared tables, temp tables, TOAST (TOAST
17774 * will be moved with the main table).
17775 *
17776 * Conflict log tables are system-managed for logical replication and
17777 * cannot be altered directly, so skip them as well; otherwise a
17778 * single such table in the source tablespace would abort the whole
17779 * bulk move.
17780 */
17781 if (IsCatalogNamespace(relForm->relnamespace) ||
17782 relForm->relisshared ||
17783 isAnyTempNamespace(relForm->relnamespace) ||
17784 IsToastNamespace(relForm->relnamespace) ||
17785 IsConflictLogTableNamespace(relForm->relnamespace))
17786 continue;
17787
17788 /* Only move the object type requested */
17789 if ((stmt->objtype == OBJECT_TABLE &&
17790 relForm->relkind != RELKIND_RELATION &&
17791 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17792 (stmt->objtype == OBJECT_INDEX &&
17793 relForm->relkind != RELKIND_INDEX &&
17794 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17795 (stmt->objtype == OBJECT_MATVIEW &&
17796 relForm->relkind != RELKIND_MATVIEW))
17797 continue;
17798
17799 /* Check if we are only moving objects owned by certain roles */
17800 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17801 continue;
17802
17803 /*
17804 * Handle permissions-checking here since we are locking the tables
17805 * and also to avoid doing a bunch of work only to fail part-way. Note
17806 * that permissions will also be checked by AlterTableInternal().
17807 *
17808 * Caller must be considered an owner on the table to move it.
17809 */
17812 NameStr(relForm->relname));
17813
17814 if (stmt->nowait &&
17816 ereport(ERROR,
17818 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17819 get_namespace_name(relForm->relnamespace),
17820 NameStr(relForm->relname))));
17821 else
17823
17824 /* Add to our list of objects to move */
17825 relations = lappend_oid(relations, relOid);
17826 }
17827
17828 table_endscan(scan);
17830
17831 if (relations == NIL)
17834 errmsg("no matching relations in tablespace \"%s\" found",
17835 orig_tablespaceoid == InvalidOid ? "(database default)" :
17837
17838 /* Everything is locked, loop through and move all of the relations. */
17839 foreach(l, relations)
17840 {
17841 List *cmds = NIL;
17843
17845 cmd->name = stmt->new_tablespacename;
17846
17847 cmds = lappend(cmds, cmd);
17848
17850 /* OID is set by AlterTableInternal */
17851 AlterTableInternal(lfirst_oid(l), cmds, false);
17853 }
17854
17855 return new_tablespaceoid;
17856}
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:3902
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4156
char * get_tablespace_name(Oid spc_oid)
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
bool IsToastNamespace(Oid namespaceId)
Definition catalog.c:277
bool IsConflictLogTableNamespace(Oid namespaceId)
Definition catalog.c:290
bool IsCatalogNamespace(Oid namespaceId)
Definition catalog.c:259
#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:1436
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:2317
Oid GetUserId(void)
Definition miscinit.c:470
bool isAnyTempNamespace(Oid namespaceId)
Definition namespace.c:3759
#define makeNode(_type_)
Definition nodes.h:159
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:4655
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(), IsConflictLogTableNamespace(), 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 19649 of file tablecmds.c.

19650{
19651 Relation rel;
19652 Oid relid;
19653 Oid oldNspOid;
19654 Oid nspOid;
19655 RangeVar *newrv;
19658
19660 stmt->missing_ok ? RVR_MISSING_OK : 0,
19662 stmt);
19663
19664 if (!OidIsValid(relid))
19665 {
19667 (errmsg("relation \"%s\" does not exist, skipping",
19668 stmt->relation->relname)));
19669 return InvalidObjectAddress;
19670 }
19671
19672 rel = relation_open(relid, NoLock);
19673
19675
19676 /* If it's an owned sequence, disallow moving it by itself. */
19677 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19678 {
19679 Oid tableId;
19680 int32 colId;
19681
19682 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19684 ereport(ERROR,
19686 errmsg("cannot move an owned sequence into another schema"),
19687 errdetail("Sequence \"%s\" is linked to table \"%s\".",
19690 }
19691
19692 /* Get and lock schema OID and check its permissions. */
19693 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19695
19696 /* common checks on switching namespaces */
19698
19702
19704
19705 if (oldschema)
19707
19708 /* close rel, but keep lock until commit */
19709 relation_close(rel, NoLock);
19710
19711 return myself;
19712}
int32_t int32
Definition c.h:679
ObjectAddresses * new_object_addresses(void)
void free_object_addresses(ObjectAddresses *addrs)
int errdetail(const char *fmt,...) pg_attribute_printf(1
RangeVar * makeRangeVar(char *schemaname, char *relname, int location)
Definition makefuncs.c:473
Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation, LOCKMODE lockmode, Oid *existing_relation_id)
Definition namespace.c:740
void CheckSetNamespace(Oid oldNspOid, Oid nspOid)
Definition namespace.c:3531
const ObjectAddress InvalidObjectAddress
bool sequenceIsOwned(Oid seqId, char deptype, Oid *tableId, int32 *colId)
Definition pg_depend.c:1032
void AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid, ObjectAddresses *objsMoved)

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

Referenced by ExecAlterObjectSchemaStmt().

◆ AlterTableNamespaceInternal()

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

Definition at line 19720 of file tablecmds.c.

19722{
19724
19725 Assert(objsMoved != NULL);
19726
19727 /* OK, modify the pg_class row and pg_depend entry */
19729
19731 nspOid, true, objsMoved);
19732
19733 /* Fix the table's row type too, if it has one */
19734 if (OidIsValid(rel->rd_rel->reltype))
19736 false, /* isImplicitArray */
19737 false, /* ignoreDependent */
19738 false, /* errorOnTableType */
19739 objsMoved);
19740
19741 /* Fix other dependent stuff */
19746 false, objsMoved);
19747
19749}
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:4195

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

Referenced by AlterObjectNamespace_oid(), and AlterTableNamespace().

◆ applyPartitionIndexExtDeps()

static void applyPartitionIndexExtDeps ( Oid  newPartOid,
List extDepState 
)
static

Definition at line 23685 of file tablecmds.c.

23686{
23687 Relation partRel;
23688 List *indexList;
23689
23690 if (extDepState == NIL)
23691 return;
23692
23693 /*
23694 * Use NoLock since the caller already holds AccessExclusiveLock on the
23695 * new partition.
23696 */
23697 partRel = table_open(newPartOid, NoLock);
23699
23700 foreach_oid(indexOid, indexList)
23701 {
23703
23704 if (!get_rel_relispartition(indexOid))
23705 continue;
23706
23707 parentIdxOid = get_partition_parent(indexOid, true);
23709 continue;
23710
23712 {
23714
23715 if (entry->parentIndexOid > parentIdxOid)
23716 break;
23717 if (entry->parentIndexOid < parentIdxOid)
23718 continue;
23719
23721
23722 foreach_oid(extOid, entry->extensionOids)
23723 {
23725
23729 }
23730 break;
23731 }
23732 }
23733
23735 table_close(partRel, NoLock);
23736}
@ DEPENDENCY_AUTO_EXTENSION
Definition dependency.h:39
bool get_rel_relispartition(Oid relid)
Definition lsyscache.c:2341
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition partition.c:53
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
#define foreach_oid(var, lst)
Definition pg_list.h:503

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

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ ATAddCheckNNConstraint()

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

Definition at line 10048 of file tablecmds.c.

10051{
10052 List *newcons;
10053 ListCell *lcon;
10054 List *children;
10055 ListCell *child;
10057
10058 /* Guard against stack overflow due to overly deep inheritance tree. */
10060
10061 /* At top level, permission check was done in ATPrepCmd, else do it */
10062 if (recursing)
10065
10066 /*
10067 * Call AddRelationNewConstraints to do the work, making sure it works on
10068 * a copy of the Constraint so transformExpr can't modify the original. It
10069 * returns a list of cooked constraints.
10070 *
10071 * If the constraint ends up getting merged with a pre-existing one, it's
10072 * omitted from the returned list, which is what we want: we do not need
10073 * to do any validation work. That can only happen at child tables,
10074 * though, since we disallow merging at the top level.
10075 */
10077 list_make1(copyObject(constr)),
10078 recursing || is_readd, /* allow_merge */
10079 !recursing, /* is_local */
10080 is_readd, /* is_internal */
10081 NULL); /* queryString not available
10082 * here */
10083
10084 /* we don't expect more than one constraint here */
10085 Assert(list_length(newcons) <= 1);
10086
10087 /* Add each to-be-validated constraint to Phase 3's queue */
10088 foreach(lcon, newcons)
10089 {
10091
10092 if (!ccon->skip_validation && ccon->contype != CONSTR_NOTNULL)
10093 {
10095
10097 newcon->name = ccon->name;
10098 newcon->contype = ccon->contype;
10099 newcon->qual = ccon->expr;
10100
10101 tab->constraints = lappend(tab->constraints, newcon);
10102 }
10103
10104 /* Save the actually assigned name if it was defaulted */
10105 if (constr->conname == NULL)
10106 constr->conname = ccon->name;
10107
10108 /*
10109 * If adding a valid not-null constraint, set the pg_attribute flag
10110 * and tell phase 3 to verify existing rows, if needed. For an
10111 * invalid constraint, just set attnotnull, without queueing
10112 * verification.
10113 */
10114 if (constr->contype == CONSTR_NOTNULL)
10115 set_attnotnull(wqueue, rel, ccon->attnum,
10116 !constr->skip_validation,
10117 !constr->skip_validation);
10118
10119 ObjectAddressSet(address, ConstraintRelationId, ccon->conoid);
10120 }
10121
10122 /* At this point we must have a locked-down name to use */
10123 Assert(newcons == NIL || constr->conname != NULL);
10124
10125 /* Advance command counter in case same table is visited multiple times */
10127
10128 /*
10129 * If the constraint got merged with an existing constraint, we're done.
10130 * We mustn't recurse to child tables in this case, because they've
10131 * already got the constraint, and visiting them again would lead to an
10132 * incorrect value for coninhcount.
10133 */
10134 if (newcons == NIL)
10135 return address;
10136
10137 /*
10138 * If adding a NO INHERIT constraint, no need to find our children.
10139 */
10140 if (constr->is_no_inherit)
10141 return address;
10142
10143 /*
10144 * Propagate to children as appropriate. Unlike most other ALTER
10145 * routines, we have to do this one level of recursion at a time; we can't
10146 * use find_all_inheritors to do it in one pass.
10147 */
10148 children =
10150
10151 /*
10152 * Check if ONLY was specified with ALTER TABLE. If so, allow the
10153 * constraint creation only if there are no children currently. Error out
10154 * otherwise.
10155 */
10156 if (!recurse && children != NIL)
10157 ereport(ERROR,
10159 errmsg("constraint must be added to child tables too")));
10160
10161 /*
10162 * Recurse to create the constraint on each child.
10163 */
10164 foreach(child, children)
10165 {
10166 Oid childrelid = lfirst_oid(child);
10169
10170 /* find_inheritance_children already got lock */
10173
10174 /* Find or create work queue entry for this table */
10176
10177 /* Recurse to this child */
10179 constr, recurse, true, is_readd, lockmode);
10180
10182 }
10183
10184 return address;
10185}
List * AddRelationNewConstraints(Relation rel, List *newColDefaults, List *newConstraints, bool allow_merge, bool is_local, bool is_internal, const char *queryString)
Definition heap.c:2405
@ CONSTR_NOTNULL
List * find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
Definition pg_inherits.c:59
static int list_length(const List *l)
Definition pg_list.h:152
#define list_make1(x1)
Definition pg_list.h:244
void check_stack_depth(void)
Definition stack_depth.c:96
ConstrType contype
bool is_no_inherit
bool skip_validation
char * conname
#define ATT_TABLE
Definition tablecmds.c:338
static ObjectAddress ATAddCheckNNConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel, Constraint *constr, bool recurse, bool recursing, bool is_readd, LOCKMODE lockmode)
static void ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets)
Definition tablecmds.c:6864
#define ATT_FOREIGN_TABLE
Definition tablecmds.c:343
#define ATT_PARTITIONED_TABLE
Definition tablecmds.c:346
static void set_attnotnull(List **wqueue, Relation rel, AttrNumber attnum, bool is_valid, bool queue_validation)
Definition tablecmds.c:7981

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

10206{
10219 bool with_period;
10221 int i;
10222 int numfks,
10223 numpks,
10225 Oid indexOid;
10226 bool old_check_ok;
10227 ObjectAddress address;
10229
10230 /*
10231 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10232 * delete rows out from under us.
10233 */
10234 if (OidIsValid(fkconstraint->old_pktable_oid))
10236 else
10238
10239 /*
10240 * Validity checks (permission checks wait till we have the column
10241 * numbers)
10242 */
10243 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10244 ereport(ERROR,
10246 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10249
10250 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10251 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10252 ereport(ERROR,
10254 errmsg("referenced relation \"%s\" is not a table",
10256
10257 /*
10258 * Conflict log tables are used internally for logical replication
10259 * conflict logging and should not be referenced by foreign keys, as it
10260 * could disrupt conflict logging.
10261 */
10262 if (IsConflictLogTableClass(pkrel->rd_rel))
10263 ereport(ERROR,
10265 errmsg("cannot reference conflict log table \"%s\"",
10267 errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
10268
10270 ereport(ERROR,
10272 errmsg("permission denied: \"%s\" is a system catalog",
10274
10275 /*
10276 * References from permanent or unlogged tables to temp tables, and from
10277 * permanent tables to unlogged tables, are disallowed because the
10278 * referenced data can vanish out from under us. References from temp
10279 * tables to any other table type are also disallowed, because other
10280 * backends might need to run the RI triggers on the perm table, but they
10281 * can't reliably see tuples in the local buffers of other backends.
10282 */
10283 switch (rel->rd_rel->relpersistence)
10284 {
10287 ereport(ERROR,
10289 errmsg("constraints on permanent tables may reference only permanent tables")));
10290 break;
10293 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10294 ereport(ERROR,
10296 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10297 break;
10299 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10300 ereport(ERROR,
10302 errmsg("constraints on temporary tables may reference only temporary tables")));
10303 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10304 ereport(ERROR,
10306 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10307 break;
10308 }
10309
10310 /*
10311 * Look up the referencing attributes to make sure they exist, and record
10312 * their attnums and type and collation OIDs.
10313 */
10315 fkconstraint->fk_attrs,
10317 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10318 if (with_period && !fkconstraint->fk_with_period)
10319 ereport(ERROR,
10321 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10322
10324 fkconstraint->fk_del_set_cols,
10329 fkconstraint->fk_del_set_cols);
10330
10331 /*
10332 * If the attribute list for the referenced table was omitted, lookup the
10333 * definition of the primary key and use it. Otherwise, validate the
10334 * supplied attribute list. In either case, discover the index OID and
10335 * index opclasses, and the attnums and type and collation OIDs of the
10336 * attributes.
10337 */
10338 if (fkconstraint->pk_attrs == NIL)
10339 {
10341 &fkconstraint->pk_attrs,
10344
10345 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10346 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10347 ereport(ERROR,
10349 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10350 }
10351 else
10352 {
10354 fkconstraint->pk_attrs,
10356
10357 /* Since we got pk_attrs, one should be a period. */
10358 if (with_period && !fkconstraint->pk_with_period)
10359 ereport(ERROR,
10361 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10362
10363 /* Look for an index matching the column list */
10366 }
10367
10368 /*
10369 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10370 * must use PERIOD.
10371 */
10373 ereport(ERROR,
10375 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10376
10377 /*
10378 * Now we can check permissions.
10379 */
10381
10382 /*
10383 * Check some things for generated columns.
10384 */
10385 for (i = 0; i < numfks; i++)
10386 {
10387 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10388
10389 if (attgenerated)
10390 {
10391 /*
10392 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10393 */
10394 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10395 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10396 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10397 ereport(ERROR,
10399 errmsg("invalid %s action for foreign key constraint containing generated column",
10400 "ON UPDATE")));
10401 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10402 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10403 ereport(ERROR,
10405 errmsg("invalid %s action for foreign key constraint containing generated column",
10406 "ON DELETE")));
10407 }
10408
10409 /*
10410 * FKs on virtual columns are not supported. This would require
10411 * various additional support in ri_triggers.c, including special
10412 * handling in ri_NullCheck(), ri_KeysEqual(),
10413 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10414 * as NULL there). Also not really practical as long as you can't
10415 * index virtual columns.
10416 */
10417 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10418 ereport(ERROR,
10420 errmsg("foreign key constraints on virtual generated columns are not supported")));
10421 }
10422
10423 /*
10424 * Some actions are currently unsupported for foreign keys using PERIOD.
10425 */
10426 if (fkconstraint->fk_with_period)
10427 {
10428 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10429 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10430 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10431 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10432 ereport(ERROR,
10434 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10435 "ON UPDATE"));
10436
10437 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10438 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10439 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10440 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10441 ereport(ERROR,
10443 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10444 "ON DELETE"));
10445 }
10446
10447 /*
10448 * Look up the equality operators to use in the constraint.
10449 *
10450 * Note that we have to be careful about the difference between the actual
10451 * PK column type and the opclass' declared input type, which might be
10452 * only binary-compatible with it. The declared opcintype is the right
10453 * thing to probe pg_amop with.
10454 */
10455 if (numfks != numpks)
10456 ereport(ERROR,
10458 errmsg("number of referencing and referenced columns for foreign key disagree")));
10459
10460 /*
10461 * On the strength of a previous constraint, we might avoid scanning
10462 * tables to validate this one. See below.
10463 */
10464 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10465 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10466
10467 for (i = 0; i < numpks; i++)
10468 {
10469 Oid pktype = pktypoid[i];
10470 Oid fktype = fktypoid[i];
10471 Oid fktyped;
10472 Oid pkcoll = pkcolloid[i];
10473 Oid fkcoll = fkcolloid[i];
10476 Oid amid;
10477 Oid opfamily;
10478 Oid opcintype;
10479 bool for_overlaps;
10480 CompareType cmptype;
10481 Oid pfeqop;
10482 Oid ppeqop;
10483 Oid ffeqop;
10486
10487 /* We need several fields out of the pg_opclass entry */
10490 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10492 amid = cla_tup->opcmethod;
10493 opfamily = cla_tup->opcfamily;
10494 opcintype = cla_tup->opcintype;
10496
10497 /*
10498 * Get strategy number from index AM.
10499 *
10500 * For a normal foreign-key constraint, this should not fail, since we
10501 * already checked that the index is unique and should therefore have
10502 * appropriate equal operators. For a period foreign key, this could
10503 * fail if we selected a non-matching exclusion constraint earlier.
10504 * (XXX Maybe we should do these lookups earlier so we don't end up
10505 * doing that.)
10506 */
10507 for_overlaps = with_period && i == numpks - 1;
10509 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10511 ereport(ERROR,
10514 ? errmsg("could not identify an overlaps operator for foreign key")
10515 : errmsg("could not identify an equality operator for foreign key"),
10516 errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10517 cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10518
10519 /*
10520 * There had better be a primary equality operator for the index.
10521 * We'll use it for PK = PK comparisons.
10522 */
10523 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10524 eqstrategy);
10525
10526 if (!OidIsValid(ppeqop))
10527 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10528 eqstrategy, opcintype, opcintype, opfamily);
10529
10530 /*
10531 * Are there equality operators that take exactly the FK type? Assume
10532 * we should look through any domain here.
10533 */
10535
10536 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10537 eqstrategy);
10538 if (OidIsValid(pfeqop))
10539 {
10542 eqstrategy);
10543 }
10544 else
10545 {
10546 /* keep compiler quiet */
10549 }
10550
10551 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10552 {
10553 /*
10554 * Otherwise, look for an implicit cast from the FK type to the
10555 * opcintype, and if found, use the primary equality operator.
10556 * This is a bit tricky because opcintype might be a polymorphic
10557 * type such as ANYARRAY or ANYENUM; so what we have to test is
10558 * whether the two actual column types can be concurrently cast to
10559 * that type. (Otherwise, we'd fail to reject combinations such
10560 * as int[] and point[].)
10561 */
10562 Oid input_typeids[2];
10564
10565 input_typeids[0] = pktype;
10566 input_typeids[1] = fktype;
10567 target_typeids[0] = opcintype;
10568 target_typeids[1] = opcintype;
10571 {
10572 pfeqop = ffeqop = ppeqop;
10573 pfeqop_right = opcintype;
10574 }
10575 }
10576
10577 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10578 ereport(ERROR,
10580 errmsg("foreign key constraint \"%s\" cannot be implemented",
10581 fkconstraint->conname),
10582 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10583 "are of incompatible types: %s and %s.",
10584 strVal(list_nth(fkconstraint->fk_attrs, i)),
10585 strVal(list_nth(fkconstraint->pk_attrs, i)),
10588
10589 /*
10590 * This shouldn't be possible, but better check to make sure we have a
10591 * consistent state for the check below.
10592 */
10594 elog(ERROR, "key columns are not both collatable");
10595
10597 {
10598 bool pkcolldet;
10599 bool fkcolldet;
10600
10603
10604 /*
10605 * SQL requires that both collations are the same. This is
10606 * because we need a consistent notion of equality on both
10607 * columns. We relax this by allowing different collations if
10608 * they are both deterministic. (This is also for backward
10609 * compatibility, because PostgreSQL has always allowed this.)
10610 */
10611 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10612 ereport(ERROR,
10614 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10615 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10616 "have incompatible collations: \"%s\" and \"%s\". "
10617 "If either collation is nondeterministic, then both collations have to be the same.",
10618 strVal(list_nth(fkconstraint->fk_attrs, i)),
10619 strVal(list_nth(fkconstraint->pk_attrs, i)),
10622 }
10623
10624 if (old_check_ok)
10625 {
10626 /*
10627 * When a pfeqop changes, revalidate the constraint. We could
10628 * permit intra-opfamily changes, but that adds subtle complexity
10629 * without any concrete benefit for core types. We need not
10630 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10631 */
10633 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10635 }
10636 if (old_check_ok)
10637 {
10647 fkattnum[i] - 1);
10648
10649 /*
10650 * Identify coercion pathways from each of the old and new FK-side
10651 * column types to the right (foreign) operand type of the pfeqop.
10652 * We may assume that pg_constraint.conkey is not changing.
10653 */
10654 old_fktype = attr->atttypid;
10657 &old_castfunc);
10659 &new_castfunc);
10660
10661 old_fkcoll = attr->attcollation;
10663
10664 /*
10665 * Upon a change to the cast from the FK column to its pfeqop
10666 * operand, revalidate the constraint. For this evaluation, a
10667 * binary coercion cast is equivalent to no cast at all. While
10668 * type implementors should design implicit casts with an eye
10669 * toward consistency of operations like equality, we cannot
10670 * assume here that they have done so.
10671 *
10672 * A function with a polymorphic argument could change behavior
10673 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10674 * when the cast destination is polymorphic, we only avoid
10675 * revalidation if the input type has not changed at all. Given
10676 * just the core data types and operator classes, this requirement
10677 * prevents no would-be optimizations.
10678 *
10679 * If the cast converts from a base type to a domain thereon, then
10680 * that domain type must be the opcintype of the unique index.
10681 * Necessarily, the primary key column must then be of the domain
10682 * type. Since the constraint was previously valid, all values on
10683 * the foreign side necessarily exist on the primary side and in
10684 * turn conform to the domain. Consequently, we need not treat
10685 * domains specially here.
10686 *
10687 * If the collation changes, revalidation is required, unless both
10688 * collations are deterministic, because those share the same
10689 * notion of equality (because texteq reduces to bitwise
10690 * equality).
10691 *
10692 * We need not directly consider the PK type. It's necessarily
10693 * binary coercible to the opcintype of the unique index column,
10694 * and ri_triggers.c will only deal with PK datums in terms of
10695 * that opcintype. Changing the opcintype also changes pfeqop.
10696 */
10700 new_fktype == old_fktype) &&
10701 (new_fkcoll == old_fkcoll ||
10703 }
10704
10708 }
10709
10710 /*
10711 * For FKs with PERIOD we need additional operators to check whether the
10712 * referencing row's range is contained by the aggregated ranges of the
10713 * referenced row(s). For rangetypes and multirangetypes this is
10714 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10715 * support for now. FKs will look these up at "runtime", but we should
10716 * make sure the lookup works here, even if we don't use the values.
10717 */
10718 if (with_period)
10719 {
10723
10726 }
10727
10728 /* First, create the constraint catalog entry itself. */
10730 fkconstraint->conname, fkconstraint, rel, pkrel,
10731 indexOid,
10732 InvalidOid, /* no parent constraint */
10733 numfks,
10734 pkattnum,
10735 fkattnum,
10741 false,
10742 with_period);
10743
10744 /* Next process the action triggers at the referenced side and recurse */
10746 indexOid,
10747 address.objectId,
10748 numfks,
10749 pkattnum,
10750 fkattnum,
10758 with_period);
10759
10760 /* Lastly create the check triggers at the referencing side and recurse */
10762 indexOid,
10763 address.objectId,
10764 numfks,
10765 pkattnum,
10766 fkattnum,
10773 lockmode,
10775 with_period);
10776
10777 /*
10778 * Done. Close pk table, but keep lock until we've committed.
10779 */
10781
10782 return address;
10783}
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 IsConflictLogTableClass(Form_pg_class reltuple)
Definition catalog.c:242
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:1261
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:1280
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition lsyscache.c:1553
Oid getBaseType(Oid typid)
Definition lsyscache.c:2837
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:737
#define RelationIsPermanent(relation)
Definition rel.h:628
#define InvalidStrategy
Definition stratnum.h:24
TupleDesc oldDesc
Definition tablecmds.c:177
bool rd_islocaltemp
Definition rel.h:61
void ReleaseSysCache(HeapTuple tuple)
Definition syscache.c:265
HeapTuple SearchSysCache1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:221
Relation table_openrv(const RangeVar *relation, LOCKMODE lockmode)
Definition table.c:83
static int validateFkOnDeleteSetColumns(int numfks, const int16 *fkattnums, int numfksetcols, int16 *fksetcolsattnums, List *fksetcols)
static CoercionPathType findFkeyCast(Oid targetTypeId, Oid sourceTypeId, Oid *funcid)
static Oid transformFkeyCheckAttrs(Relation pkrel, int numattrs, int16 *attnums, bool with_period, Oid *opclasses, bool *pk_has_without_overlaps)
static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts)
static int transformFkeyGetPrimaryKey(Relation pkrel, Oid *indexOid, List **attnamelist, int16 *attnums, Oid *atttypids, Oid *attcollids, Oid *opclasses, bool *pk_has_without_overlaps)
static int transformColumnNameList(Oid relId, List *colList, int16 *attnums, Oid *atttypids, Oid *attcollids)
static FormData_pg_attribute * TupleDescAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:178
#define strVal(v)
Definition value.h:82

References addFkBothSides, addFkConstraint(), addFkRecurseReferenced(), addFkRecurseReferencing(), allowSystemTableMods, Assert, can_coerce_type(), checkFkeyPermissions(), COERCION_IMPLICIT, COMPARE_EQ, COMPARE_OVERLAP, elog, ereport, errcode(), errdetail(), errmsg, ERROR, fb(), findFkeyCast(), FindFKPeriodOpers(), FKCONSTR_ACTION_CASCADE, FKCONSTR_ACTION_RESTRICT, FKCONSTR_ACTION_SETDEFAULT, FKCONSTR_ACTION_SETNULL, Form_pg_opclass, format_type_be(), get_am_name(), get_collation_isdeterministic(), get_collation_name(), get_opfamily_member(), get_opfamily_name(), getBaseType(), GETSTRUCT(), HeapTupleIsValid, i, INDEX_MAX_KEYS, IndexAmTranslateCompareType(), InvalidOid, InvalidStrategy, IsConflictLogTableClass(), 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().

◆ ATCheckCheckConstrHasEnforcedParent()

static bool ATCheckCheckConstrHasEnforcedParent ( Relation  conrel,
Relation  rel,
HeapTuple  contuple,
List changing_conids,
Oid enforced_parentoid 
)
static

Definition at line 12961 of file tablecmds.c.

12965{
12968 SysScanDesc scan;
12971
12972 /* Since this function recurses, it could be driven to stack overflow */
12974
12976 Assert(currcon->contype == CONSTRAINT_CHECK);
12977
12978 if (currcon->coninhcount <= 0)
12979 return false;
12980
12982
12988 true, NULL, 1, &skey);
12989
12991 {
12992 Oid parentoid;
12995 ScanKeyData pkey[3];
12996 HeapTuple parenttup;
12997
12999
13000 ScanKeyInit(&pkey[0],
13004 ScanKeyInit(&pkey[1],
13008 ScanKeyInit(&pkey[2],
13011 NameGetDatum(&currcon->conname));
13012
13014 true, NULL, 3, pkey);
13015
13016 /*
13017 * ConstraintRelidTypidNameIndexId is unique on (conrelid, contypid,
13018 * conname), so this loop body executes at most once per parent.
13019 */
13020 while (HeapTupleIsValid(parenttup = systable_getnext(pscan)))
13021 {
13023
13024 parentcon = (Form_pg_constraint) GETSTRUCT(parenttup);
13025
13026 if (parentcon->contype != CONSTRAINT_CHECK ||
13027 parentcon->connoinherit ||
13028 !parentcon->conenforced)
13029 continue;
13030
13031 if (!constraints_equivalent(parenttup, contuple,
13033 elog(ERROR, "child table \"%s\" has different definition for check constraint \"%s\"",
13035 NameStr(parentcon->conname));
13036
13037 /*
13038 * A parent listed in changing_conids is being changed by the same
13039 * ALTER, but it may not have been updated yet. For regular
13040 * inheritance, recurse upward to check whether an equivalent
13041 * enforced parent outside the ALTER will make it remain enforced.
13042 * Partitions cannot have multiple parents, so they do not need
13043 * this check.
13044 */
13045 if (!rel->rd_rel->relispartition &&
13047 {
13049
13050 if (parentrel == NULL)
13052
13054 parentrel,
13055 parenttup,
13058 continue;
13059 }
13060
13062 if (parentrel != NULL)
13065 systable_endscan(scan);
13067 return true;
13068 }
13069
13070 if (parentrel != NULL)
13073 }
13074
13075 systable_endscan(scan);
13077
13078 return false;
13079}
END_CATALOG_STRUCT typedef FormData_pg_inherits * Form_pg_inherits
Definition pg_inherits.h:49
static Datum NameGetDatum(const NameData *X)
Definition postgres.h:406
static bool ATCheckCheckConstrHasEnforcedParent(Relation conrel, Relation rel, HeapTuple contuple, List *changing_conids, Oid *enforced_parentoid)
static bool constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)

References AccessShareLock, Assert, ATCheckCheckConstrHasEnforcedParent(), BTEqualStrategyNumber, check_stack_depth(), constraints_equivalent(), elog, ERROR, fb(), Form_pg_constraint, Form_pg_inherits, GETSTRUCT(), HeapTupleIsValid, InvalidOid, list_member_oid(), NameGetDatum(), NameStr, NoLock, ObjectIdGetDatum(), RelationData::rd_rel, RelationGetDescr, RelationGetRelationName, RelationGetRelid, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), and table_open().

Referenced by ATCheckCheckConstrHasEnforcedParent(), and ATExecAlterCheckConstrEnforceability().

◆ ATCheckPartitionsNotInUse()

static void ATCheckPartitionsNotInUse ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 7002 of file tablecmds.c.

7003{
7004 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
7005 {
7006 List *inh;
7007 ListCell *cell;
7008
7009 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
7010 /* first element is the parent rel; must ignore it */
7011 for_each_from(cell, inh, 1)
7012 {
7014
7015 /* find_all_inheritors already got lock */
7019 }
7020 list_free(inh);
7021 }
7022}
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 15216 of file tablecmds.c.

15217{
15218 Assert(expr != NULL);
15219
15220 for (;;)
15221 {
15222 /* only one varno, so no need to check that */
15223 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
15224 return false;
15225 else if (IsA(expr, RelabelType))
15226 expr = (Node *) ((RelabelType *) expr)->arg;
15227 else if (IsA(expr, CoerceToDomain))
15228 {
15229 CoerceToDomain *d = (CoerceToDomain *) expr;
15230
15232 return true;
15233 expr = (Node *) d->arg;
15234 }
15235 else if (IsA(expr, FuncExpr))
15236 {
15237 FuncExpr *f = (FuncExpr *) expr;
15238
15239 switch (f->funcid)
15240 {
15244 return true;
15245 else
15246 expr = linitial(f->args);
15247 break;
15248 default:
15249 return true;
15250 }
15251 }
15252 else
15253 return true;
15254 }
15255}
bool TimestampTimestampTzRequiresRewrite(void)
Definition timestamp.c:6456
Datum arg
Definition elog.c:1323
#define linitial(l)
Definition pg_list.h:178
Oid funcid
Definition primnodes.h:770
List * args
Definition primnodes.h:788
bool DomainHasConstraints(Oid type_id, bool *has_volatile)
Definition typcache.c:1497

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

4970{
4971 List *wqueue = NIL;
4972 ListCell *lcmd;
4973
4974 /* Phase 1: preliminary examination of commands, create work queue */
4975 foreach(lcmd, cmds)
4976 {
4978
4979 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4980 }
4981
4982 /* Close the relation, but keep lock until commit */
4983 relation_close(rel, NoLock);
4984
4985 /* Phase 2: update system catalogs */
4986 ATRewriteCatalogs(&wqueue, lockmode, context);
4987
4988 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4989 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4990}
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:5405
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:5002
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:5957

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

22742{
22743 List *constraints;
22744 ListCell *cell;
22745
22746 constraints = GetParentedForeignKeyRefs(partition);
22747
22748 foreach(cell, constraints)
22749 {
22750 Oid constrOid = lfirst_oid(cell);
22751 HeapTuple tuple;
22753 Relation rel;
22754 Trigger trig = {0};
22755
22757 if (!HeapTupleIsValid(tuple))
22758 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22760
22761 Assert(OidIsValid(constrForm->conparentid));
22763
22764 /* prevent data changes into the referencing table until commit */
22765 rel = table_open(constrForm->conrelid, ShareLock);
22766
22767 trig.tgoid = InvalidOid;
22768 trig.tgname = NameStr(constrForm->conname);
22769 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
22770 trig.tgisinternal = true;
22771 trig.tgconstrrelid = RelationGetRelid(partition);
22772 trig.tgconstrindid = constrForm->conindid;
22773 trig.tgconstraint = constrForm->oid;
22774 trig.tgdeferrable = false;
22775 trig.tginitdeferred = false;
22776 /* we needn't fill in remaining fields */
22777
22779
22780 ReleaseSysCache(tuple);
22781
22782 table_close(rel, NoLock);
22783 }
22784}
#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 20162 of file tablecmds.c.

20164{
20166
20167 foreach(cur_item, on_commits)
20168 {
20170
20171 if (!isCommit && oc->creating_subid == mySubid)
20172 {
20173 /* cur_item must be removed */
20175 pfree(oc);
20176 }
20177 else
20178 {
20179 /* cur_item must be preserved */
20180 if (oc->creating_subid == mySubid)
20181 oc->creating_subid = parentSubid;
20182 if (oc->deleting_subid == mySubid)
20183 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
20184 }
20185 }
20186}
#define InvalidSubTransactionId
Definition c.h:801
#define foreach_delete_current(lst, var_or_cell)
Definition pg_list.h:423
static List * on_commits
Definition tablecmds.c:135

References fb(), foreach_delete_current, InvalidSubTransactionId, lfirst, on_commits, and pfree().

Referenced by AbortSubTransaction(), and CommitSubTransaction().

◆ AtEOXact_on_commit_actions()

void AtEOXact_on_commit_actions ( bool  isCommit)

Definition at line 20130 of file tablecmds.c.

20131{
20133
20134 foreach(cur_item, on_commits)
20135 {
20137
20138 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
20139 oc->creating_subid != InvalidSubTransactionId)
20140 {
20141 /* cur_item must be removed */
20143 pfree(oc);
20144 }
20145 else
20146 {
20147 /* cur_item must be preserved */
20148 oc->creating_subid = InvalidSubTransactionId;
20149 oc->deleting_subid = InvalidSubTransactionId;
20150 }
20151 }
20152}

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

7362{
7364 ColumnDef *colDef = castNode(ColumnDef, (*cmd)->def);
7365 bool if_not_exists = (*cmd)->missing_ok;
7367 attrdesc;
7371 int newattnum;
7372 char relkind;
7373 Expr *defval;
7374 List *children;
7375 ListCell *child;
7377 ObjectAddress address;
7378 TupleDesc tupdesc;
7379
7380 /* since this function recurses, it could be driven to stack overflow */
7382
7383 /* At top level, permission check was done in ATPrepCmd, else do it */
7384 if (recursing)
7385 ATSimplePermissions((*cmd)->subtype, rel,
7387
7388 if (rel->rd_rel->relispartition && !recursing)
7389 ereport(ERROR,
7391 errmsg("cannot add column to a partition")));
7392
7394
7395 /*
7396 * Are we adding the column to a recursion child? If so, check whether to
7397 * merge with an existing definition for the column. If we do merge, we
7398 * must not recurse. Children will already have the column, and recursing
7399 * into them would mess up attinhcount.
7400 */
7401 if (colDef->inhcount > 0)
7402 {
7403 HeapTuple tuple;
7404
7405 /* Does child already have a column by this name? */
7406 tuple = SearchSysCacheCopyAttName(myrelid, colDef->colname);
7407 if (HeapTupleIsValid(tuple))
7408 {
7410 Oid ctypeId;
7411 int32 ctypmod;
7412 Oid ccollid;
7413
7414 /* Child column must match on type, typmod, and collation */
7416 if (ctypeId != childatt->atttypid ||
7417 ctypmod != childatt->atttypmod)
7418 ereport(ERROR,
7420 errmsg("child table \"%s\" has different type for column \"%s\"",
7421 RelationGetRelationName(rel), colDef->colname)));
7423 if (ccollid != childatt->attcollation)
7424 ereport(ERROR,
7426 errmsg("child table \"%s\" has different collation for column \"%s\"",
7427 RelationGetRelationName(rel), colDef->colname),
7428 errdetail("\"%s\" versus \"%s\"",
7430 get_collation_name(childatt->attcollation))));
7431
7432 /* Bump the existing child att's inhcount */
7433 if (pg_add_s16_overflow(childatt->attinhcount, 1,
7434 &childatt->attinhcount))
7435 ereport(ERROR,
7437 errmsg("too many inheritance parents"));
7438 CatalogTupleUpdate(attrdesc, &tuple->t_self, tuple);
7439
7440 heap_freetuple(tuple);
7441
7442 /* Inform the user about the merge */
7444 (errmsg("merging definition of column \"%s\" for child \"%s\"",
7445 colDef->colname, RelationGetRelationName(rel))));
7446
7448
7449 /* Make the child column change visible */
7451
7452 return InvalidObjectAddress;
7453 }
7454 }
7455
7456 /* skip if the name already exists and if_not_exists is true */
7457 if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
7458 {
7460 return InvalidObjectAddress;
7461 }
7462
7463 /*
7464 * Okay, we need to add the column, so go ahead and do parse
7465 * transformation. This can result in queueing up, or even immediately
7466 * executing, subsidiary operations (such as creation of unique indexes);
7467 * so we mustn't do it until we have made the if_not_exists check.
7468 *
7469 * When recursing, the command was already transformed and we needn't do
7470 * so again. Also, if context isn't given we can't transform. (That
7471 * currently happens only for AT_AddColumnToView; we expect that view.c
7472 * passed us a ColumnDef that doesn't need work.)
7473 */
7474 if (context != NULL && !recursing)
7475 {
7476 *cmd = ATParseTransformCmd(wqueue, tab, rel, *cmd, recurse, lockmode,
7477 cur_pass, context);
7478 Assert(*cmd != NULL);
7479 colDef = castNode(ColumnDef, (*cmd)->def);
7480 }
7481
7482 /*
7483 * Regular inheritance children are independent enough not to inherit the
7484 * identity column from parent hence cannot recursively add identity
7485 * column if the table has inheritance children.
7486 *
7487 * Partitions, on the other hand, are integral part of a partitioned table
7488 * and inherit identity column. Hence propagate identity column down the
7489 * partition hierarchy.
7490 */
7491 if (colDef->identity &&
7492 recurse &&
7493 rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
7495 ereport(ERROR,
7497 errmsg("cannot recursively add identity column to table that has child tables")));
7498
7500
7503 elog(ERROR, "cache lookup failed for relation %u", myrelid);
7505 relkind = relform->relkind;
7506
7507 /* Determine the new attribute's number */
7508 newattnum = relform->relnatts + 1;
7510 ereport(ERROR,
7512 errmsg("tables can have at most %d columns",
7514
7515 /*
7516 * Construct new attribute's pg_attribute entry.
7517 */
7519
7520 attribute = TupleDescAttr(tupdesc, 0);
7521
7522 /* Fix up attribute number */
7523 attribute->attnum = newattnum;
7524
7525 /* make sure datatype is legal for a column */
7526 CheckAttributeType(NameStr(attribute->attname), attribute->atttypid, attribute->attcollation,
7527 list_make1_oid(rel->rd_rel->reltype),
7529
7531
7533
7534 /*
7535 * Update pg_class tuple as appropriate
7536 */
7537 relform->relnatts = newattnum;
7538
7540
7542
7543 /* Post creation hook for new attribute */
7545
7547
7548 /* Make the attribute's catalog entry visible */
7550
7551 /*
7552 * Store the DEFAULT, if any, in the catalogs
7553 */
7554 if (colDef->raw_default)
7555 {
7557
7559 rawEnt->attnum = attribute->attnum;
7560 rawEnt->raw_default = copyObject(colDef->raw_default);
7561 rawEnt->generated = colDef->generated;
7562
7563 /*
7564 * This function is intended for CREATE TABLE, so it processes a
7565 * _list_ of defaults, but we just do one.
7566 */
7568 false, true, false, NULL);
7569
7570 /* Make the additional catalog changes visible */
7572 }
7573
7574 /*
7575 * Tell Phase 3 to fill in the default expression, if there is one.
7576 *
7577 * If there is no default, Phase 3 doesn't have to do anything, because
7578 * that effectively means that the default is NULL. The heap tuple access
7579 * routines always check for attnum > # of attributes in tuple, and return
7580 * NULL if so, so without any modification of the tuple data we will get
7581 * the effect of NULL values in the new column.
7582 *
7583 * An exception occurs when the new column is of a domain type: the domain
7584 * might have a not-null constraint, or a check constraint that indirectly
7585 * rejects nulls. If there are any domain constraints then we construct
7586 * an explicit NULL default value that will be passed through
7587 * CoerceToDomain processing. (This is a tad inefficient, since it causes
7588 * rewriting the table which we really wouldn't have to do; but we do it
7589 * to preserve the historical behavior that such a failure will be raised
7590 * only if the table currently contains some rows.)
7591 *
7592 * Note: we use build_column_default, and not just the cooked default
7593 * returned by AddRelationNewConstraints, so that the right thing happens
7594 * when a datatype's default applies.
7595 *
7596 * Note: it might seem that this should happen at the end of Phase 2, so
7597 * that the effects of subsequent subcommands can be taken into account.
7598 * It's intentional that we do it now, though. The new column should be
7599 * filled according to what is said in the ADD COLUMN subcommand, so that
7600 * the effects are the same as if this subcommand had been run by itself
7601 * and the later subcommands had been issued in new ALTER TABLE commands.
7602 *
7603 * We can skip this entirely for relations without storage, since Phase 3
7604 * is certainly not going to touch them.
7605 */
7606 if (RELKIND_HAS_STORAGE(relkind))
7607 {
7609 bool has_missing = false;
7610
7611 /*
7612 * For an identity column, we can't use build_column_default(),
7613 * because the sequence ownership isn't set yet. So do it manually.
7614 */
7615 if (colDef->identity)
7616 {
7618
7619 nve->seqid = RangeVarGetRelid(colDef->identitySequence, NoLock, false);
7620 nve->typeId = attribute->atttypid;
7621
7622 defval = (Expr *) nve;
7623 }
7624 else
7625 defval = (Expr *) build_column_default(rel, attribute->attnum);
7626
7627 /* Build CoerceToDomain(NULL) expression if needed */
7629 if (!defval && has_domain_constraints)
7630 {
7634
7635 baseTypeMod = attribute->atttypmod;
7639 defval = (Expr *) coerce_to_target_type(NULL,
7640 (Node *) defval,
7641 baseTypeId,
7642 attribute->atttypid,
7643 attribute->atttypmod,
7646 -1);
7647 if (defval == NULL) /* should not happen */
7648 elog(ERROR, "failed to coerce base type to domain");
7649 }
7650
7651 if (defval)
7652 {
7654
7655 /* Prepare defval for execution, either here or in Phase 3 */
7656 defval = expression_planner(defval);
7657
7658 /* Add the new default to the newvals list */
7660 newval->attnum = attribute->attnum;
7661 newval->expr = defval;
7662 newval->is_generated = (colDef->generated != '\0');
7663
7664 tab->newvals = lappend(tab->newvals, newval);
7665
7666 /*
7667 * Attempt to skip a complete table rewrite by storing the
7668 * specified DEFAULT value outside of the heap. This is only
7669 * allowed for plain relations and non-generated columns, and the
7670 * default expression can't be volatile (stable is OK). Note that
7671 * contain_volatile_functions deems CoerceToDomain immutable, but
7672 * here we consider that coercion to a domain with constraints is
7673 * volatile; else it might fail even when the table is empty.
7674 */
7675 if (rel->rd_rel->relkind == RELKIND_RELATION &&
7676 !colDef->generated &&
7678 !contain_volatile_functions((Node *) defval))
7679 {
7680 EState *estate;
7683 bool missingIsNull;
7684
7685 /* Evaluate the default expression */
7686 estate = CreateExecutorState();
7687 exprState = ExecPrepareExpr(defval, estate);
7689 GetPerTupleExprContext(estate),
7690 &missingIsNull);
7691 /* If it turns out NULL, nothing to do; else store it */
7692 if (!missingIsNull)
7693 {
7695 /* Make the additional catalog change visible */
7697 has_missing = true;
7698 }
7699 FreeExecutorState(estate);
7700 }
7701 else
7702 {
7703 /*
7704 * Failed to use missing mode. We have to do a table rewrite
7705 * to install the value --- unless it's a virtual generated
7706 * column.
7707 */
7708 if (colDef->generated != ATTRIBUTE_GENERATED_VIRTUAL)
7710 }
7711 }
7712
7713 if (!has_missing)
7714 {
7715 /*
7716 * If the new column is NOT NULL, and there is no missing value,
7717 * tell Phase 3 it needs to check for NULLs.
7718 */
7719 tab->verify_new_notnull |= colDef->is_not_null;
7720 }
7721 }
7722
7723 /*
7724 * Add needed dependency entries for the new column.
7725 */
7728
7729 /*
7730 * Propagate to children as appropriate. Unlike most other ALTER
7731 * routines, we have to do this one level of recursion at a time; we can't
7732 * use find_all_inheritors to do it in one pass.
7733 */
7734 children =
7736
7737 /*
7738 * If we are told not to recurse, there had better not be any child
7739 * tables; else the addition would put them out of step.
7740 */
7741 if (children && !recurse)
7742 ereport(ERROR,
7744 errmsg("column must be added to child tables too")));
7745
7746 /* Children should see column as singly inherited */
7747 if (!recursing)
7748 {
7749 childcmd = copyObject(*cmd);
7751 colDef->inhcount = 1;
7752 colDef->is_local = false;
7753 }
7754 else
7755 childcmd = *cmd; /* no need to copy again */
7756
7757 foreach(child, children)
7758 {
7759 Oid childrelid = lfirst_oid(child);
7762
7763 /* find_inheritance_children already got lock */
7766
7767 /* Find or create work queue entry for this table */
7769
7770 /* Recurse to child; return value is ignored */
7772 &childcmd, recurse, true,
7773 lockmode, cur_pass, context);
7774
7776 }
7777
7779 return address;
7780}
bool contain_volatile_functions(Node *clause)
Definition clauses.c:567
#define AT_REWRITE_DEFAULT_VAL
ExprState * ExecPrepareExpr(Expr *node, EState *estate)
Definition execExpr.c:765
void FreeExecutorState(EState *estate)
Definition execUtils.c:197
EState * CreateExecutorState(void)
Definition execUtils.c:90
#define GetPerTupleExprContext(estate)
Definition executor.h:665
static Datum ExecEvalExpr(ExprState *state, ExprContext *econtext, bool *isNull)
Definition executor.h:401
#define palloc_object(type)
Definition fe_memutils.h:89
#define newval
void CheckAttributeType(const char *attname, Oid atttypid, Oid attcollation, List *containing_rowtypes, int flags)
Definition heap.c:549
void InsertPgAttributeTuples(Relation pg_attribute_rel, TupleDesc tupdesc, Oid new_rel_oid, const FormExtraData_pg_attribute tupdesc_extra[], CatalogIndexState indstate)
Definition heap.c:732
void StoreAttrMissingVal(Relation rel, AttrNumber attnum, Datum missingval)
Definition heap.c:2050
#define CHKATYPE_IS_VIRTUAL
Definition heap.h:26
#define MaxHeapAttributeNumber
static bool pg_add_s16_overflow(int16 a, int16 b, int16 *result)
Definition int.h:67
Oid get_typcollation(Oid typid)
Definition lsyscache.c:3372
Oid getBaseTypeAndTypmod(Oid typid, int32 *typmod)
Definition lsyscache.c:2854
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:180
#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:7010
uint64_t Datum
Definition postgres.h:70
@ COERCE_IMPLICIT_CAST
Definition primnodes.h:759
@ COERCION_ASSIGNMENT
Definition primnodes.h:738
Node * build_column_default(Relation rel, int attrno)
bool verify_new_notnull
Definition tablecmds.c:194
ItemPointerData t_self
Definition htup.h:65
HeapTuple SearchSysCacheCopyAttName(Oid relid, const char *attname)
Definition syscache.c:499
#define SearchSysCacheCopy1(cacheId, key1)
Definition syscache.h:91
static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid)
Definition tablecmds.c:7840
static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid)
Definition tablecmds.c:7858
TupleDesc BuildDescForRelation(const List *columns)
Definition tablecmds.c:1437
static AlterTableCmd * ATParseTransformCmd(List **wqueue, AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition tablecmds.c:5830
static bool check_for_column_name_collision(Relation rel, const char *colname, bool if_not_exists)
Definition tablecmds.c:7787
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:7358

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

Referenced by ATExecAddColumn(), and ATExecCmd().

◆ ATExecAddConstraint()

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

Definition at line 9932 of file tablecmds.c.

9935{
9937
9939
9940 /*
9941 * Currently, we only expect to see CONSTR_CHECK, CONSTR_NOTNULL and
9942 * CONSTR_FOREIGN nodes arriving here (see the preprocessing done in
9943 * parse_utilcmd.c).
9944 */
9945 switch (newConstraint->contype)
9946 {
9947 case CONSTR_CHECK:
9948 case CONSTR_NOTNULL:
9949 address =
9951 newConstraint, recurse, false, is_readd,
9952 lockmode);
9953 break;
9954
9955 case CONSTR_FOREIGN:
9956
9957 /*
9958 * Assign or validate constraint name
9959 */
9960 if (newConstraint->conname)
9961 {
9963 RelationGetRelid(rel),
9964 newConstraint->conname))
9965 ereport(ERROR,
9967 errmsg("constraint \"%s\" for relation \"%s\" already exists",
9968 newConstraint->conname,
9970 }
9971 else
9972 newConstraint->conname =
9975 "fkey",
9977 NIL);
9978
9979 address = ATAddForeignKeyConstraint(wqueue, tab, rel,
9981 recurse, false,
9982 lockmode);
9983 break;
9984
9985 default:
9986 elog(ERROR, "unrecognized constraint type: %d",
9987 (int) newConstraint->contype);
9988 }
9989
9990 return address;
9991}
@ CONSTR_CHECK
#define ERRCODE_DUPLICATE_OBJECT
Definition streamutil.c:30
static char * ChooseForeignKeyConstraintNameAddition(List *colnames)
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 8381 of file tablecmds.c.

8383{
8385 HeapTuple tuple;
8388 ObjectAddress address;
8390 bool ispartitioned;
8391
8392 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8393 if (ispartitioned && !recurse)
8394 ereport(ERROR,
8396 errmsg("cannot add identity to a column of only the partitioned table"),
8397 errhint("Do not specify the ONLY keyword.")));
8398
8399 if (rel->rd_rel->relispartition && !recursing)
8400 ereport(ERROR,
8402 errmsg("cannot add identity to a column of a partition"));
8403
8405
8407 if (!HeapTupleIsValid(tuple))
8408 ereport(ERROR,
8410 errmsg("column \"%s\" of relation \"%s\" does not exist",
8413 attnum = attTup->attnum;
8414
8415 /* Can't alter a system attribute */
8416 if (attnum <= 0)
8417 ereport(ERROR,
8419 errmsg("cannot alter system column \"%s\"",
8420 colName)));
8421
8422 /*
8423 * Creating a column as identity implies NOT NULL, so adding the identity
8424 * to an existing column that is not NOT NULL would create a state that
8425 * cannot be reproduced without contortions.
8426 */
8427 if (!attTup->attnotnull)
8428 ereport(ERROR,
8430 errmsg("column \"%s\" of relation \"%s\" must be declared NOT NULL before identity can be added",
8432
8433 /*
8434 * On the other hand, if a not-null constraint exists, then verify that
8435 * it's compatible.
8436 */
8437 if (attTup->attnotnull)
8438 {
8441
8443 attnum);
8445 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
8447
8449 if (!conForm->convalidated)
8450 ereport(ERROR,
8452 errmsg("incompatible NOT VALID constraint \"%s\" on relation \"%s\"",
8453 NameStr(conForm->conname), RelationGetRelationName(rel)),
8454 errhint("You might need to validate it using %s.",
8455 "ALTER TABLE ... VALIDATE CONSTRAINT"));
8456 }
8457
8458 if (attTup->attidentity)
8459 ereport(ERROR,
8461 errmsg("column \"%s\" of relation \"%s\" is already an identity column",
8463
8464 if (attTup->atthasdef)
8465 ereport(ERROR,
8467 errmsg("column \"%s\" of relation \"%s\" already has a default value",
8469
8470 attTup->attidentity = cdef->identity;
8471 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8472
8474 RelationGetRelid(rel),
8475 attTup->attnum);
8477 RelationGetRelid(rel), attnum);
8478 heap_freetuple(tuple);
8479
8481
8482 /*
8483 * Recurse to propagate the identity column to partitions. Identity is
8484 * not inherited in regular inheritance children.
8485 */
8486 if (recurse && ispartitioned)
8487 {
8488 List *children;
8489 ListCell *lc;
8490
8491 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8492
8493 foreach(lc, children)
8494 {
8496
8498 ATExecAddIdentity(childrel, colName, def, lockmode, recurse, true);
8500 }
8501 }
8502
8503 return address;
8504}
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:8381

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

9757{
9758 bool check_rights;
9759 bool skip_build;
9760 bool quiet;
9761 ObjectAddress address;
9762
9764 Assert(!stmt->concurrent);
9765
9766 /* The IndexStmt has already been through transformIndexStmt */
9767 Assert(stmt->transformed);
9768
9769 /* suppress schema rights check when rebuilding existing index */
9771 /* skip index build if phase 3 will do it or we're reusing an old one */
9772 skip_build = tab->rewrite > 0 || RelFileNumberIsValid(stmt->oldNumber);
9773 /* suppress notices when rebuilding existing index */
9774 quiet = is_rebuild;
9775
9776 address = DefineIndex(NULL,
9777 RelationGetRelid(rel),
9778 stmt,
9779 InvalidOid, /* no predefined OID */
9780 InvalidOid, /* no parent index */
9781 InvalidOid, /* no parent constraint */
9782 -1, /* total_parts unknown */
9783 true, /* is_alter_table */
9785 false, /* check_not_in_use - we did it already */
9786 skip_build,
9787 quiet);
9788
9789 /*
9790 * If TryReuseIndex() stashed a relfilenumber for us, we used it for the
9791 * new index instead of building from scratch. Restore associated fields.
9792 * This may store InvalidSubTransactionId in both fields, in which case
9793 * relcache.c will assume it can rebuild the relcache entry. Hence, do
9794 * this after the CCI that made catalog rows visible to any rebuild. The
9795 * DROP of the old edition of this index will have scheduled the storage
9796 * for deletion at commit, so cancel that pending deletion.
9797 */
9798 if (RelFileNumberIsValid(stmt->oldNumber))
9799 {
9800 Relation irel = index_open(address.objectId, NoLock);
9801
9802 irel->rd_createSubid = stmt->oldCreateSubid;
9803 irel->rd_firstRelfilelocatorSubid = stmt->oldFirstRelfilelocatorSubid;
9805 index_close(irel, NoLock);
9806 }
9807
9808 return address;
9809}
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:555
#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 9840 of file tablecmds.c.

9842{
9843 Oid index_oid = stmt->indexOid;
9844 Relation indexRel;
9845 char *indexName;
9846 IndexInfo *indexInfo;
9847 char *constraintName;
9848 char constraintType;
9849 ObjectAddress address;
9850 uint16 flags;
9851
9854 Assert(stmt->isconstraint);
9855
9856 /*
9857 * Doing this on partitioned tables is not a simple feature to implement,
9858 * so let's punt for now.
9859 */
9860 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9861 ereport(ERROR,
9863 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9864
9866
9868
9869 indexInfo = BuildIndexInfo(indexRel);
9870
9871 /* this should have been checked at parse time */
9872 if (!indexInfo->ii_Unique)
9873 elog(ERROR, "index \"%s\" is not unique", indexName);
9874
9875 /*
9876 * Determine name to assign to constraint. We require a constraint to
9877 * have the same name as the underlying index; therefore, use the index's
9878 * existing name as the default constraint name, and if the user
9879 * explicitly gives some other name for the constraint, rename the index
9880 * to match.
9881 */
9882 constraintName = stmt->idxname;
9883 if (constraintName == NULL)
9885 else if (strcmp(constraintName, indexName) != 0)
9886 {
9888 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9891 }
9892
9893 /* Extra checks needed if making primary key */
9894 if (stmt->primary)
9895 index_check_primary_key(rel, indexInfo, true, stmt);
9896
9897 /* Note we currently don't support EXCLUSION constraints here */
9898 if (stmt->primary)
9900 else
9902
9903 /* Create the catalog entries for the constraint */
9906 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9907 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9909
9910 address = index_constraint_create(rel,
9911 index_oid,
9912 InvalidOid,
9913 indexInfo,
9916 flags,
9918 false); /* is_internal */
9919
9920 index_close(indexRel, NoLock);
9921
9922 return address;
9923}
uint16_t uint16
Definition c.h:682
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:4362

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

17969{
17971 List *children;
17972 ObjectAddress address;
17973 const char *trigger_name;
17974
17975 /*
17976 * A self-exclusive lock is needed here. See the similar case in
17977 * MergeAttributes() for a full explanation.
17978 */
17980
17981 /*
17982 * Must be owner of both parent and child -- child was checked by
17983 * ATSimplePermissions call in ATPrepCmd
17984 */
17987
17988 /* Permanent rels cannot inherit from temporary ones */
17989 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17990 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17991 ereport(ERROR,
17993 errmsg("cannot inherit from temporary relation \"%s\"",
17995
17996 /* If parent rel is temp, it must belong to this session */
17998 ereport(ERROR,
18000 errmsg("cannot inherit from temporary relation of another session")));
18001
18002 /* Ditto for the child */
18004 ereport(ERROR,
18006 errmsg("cannot inherit to temporary relation of another session")));
18007
18008 /* Prevent partitioned tables from becoming inheritance parents */
18009 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18010 ereport(ERROR,
18012 errmsg("cannot inherit from partitioned table \"%s\"",
18013 parent->relname)));
18014
18015 /* Likewise for partitions */
18016 if (parent_rel->rd_rel->relispartition)
18017 ereport(ERROR,
18019 errmsg("cannot inherit from a partition")));
18020
18021 /*
18022 * Prevent circularity by seeing if proposed parent inherits from child.
18023 * (In particular, this disallows making a rel inherit from itself.)
18024 *
18025 * This is not completely bulletproof because of race conditions: in
18026 * multi-level inheritance trees, someone else could concurrently be
18027 * making another inheritance link that closes the loop but does not join
18028 * either of the rels we have locked. Preventing that seems to require
18029 * exclusive locks on the entire inheritance tree, which is a cure worse
18030 * than the disease. find_all_inheritors() will cope with circularity
18031 * anyway, so don't sweat it too much.
18032 *
18033 * We use weakest lock we can on child's children, namely AccessShareLock.
18034 */
18037
18039 ereport(ERROR,
18041 errmsg("circular inheritance not allowed"),
18042 errdetail("\"%s\" is already a child of \"%s\".",
18043 parent->relname,
18045
18046 /*
18047 * If child_rel has row-level triggers with transition tables, we
18048 * currently don't allow it to become an inheritance child. See also
18049 * prohibitions in ATExecAttachPartition() and CreateTrigger().
18050 */
18052 if (trigger_name != NULL)
18053 ereport(ERROR,
18055 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
18057 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
18058
18059 /* OK to create inheritance */
18061
18064
18065 /* keep our lock on the parent relation until commit */
18067
18068 return address;
18069}
#define RELATION_IS_OTHER_TEMP(relation)
Definition rel.h:678
char * relname
Definition primnodes.h:84
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
const char * FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc)
Definition trigger.c:2304

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

18920{
18921 Oid relid = RelationGetRelid(rel);
18924 Oid typeid;
18927 SysScanDesc scan;
18930 type_attno;
18934 typeobj;
18936
18937 /* Validate the type. */
18938 typetuple = typenameType(NULL, ofTypename, NULL);
18941 typeid = typeform->oid;
18942
18943 /* Fail if the table has any inheritance parents. */
18945 ScanKeyInit(&key,
18948 ObjectIdGetDatum(relid));
18950 true, NULL, 1, &key);
18952 ereport(ERROR,
18954 errmsg("typed tables cannot inherit")));
18955 systable_endscan(scan);
18957
18958 /*
18959 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18960 * require that the order also match. However, attnotnull need not match.
18961 */
18964 table_attno = 1;
18965 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18966 {
18968 table_attr;
18969 const char *type_attname,
18971
18972 /* Get the next non-dropped type attribute. */
18974 if (type_attr->attisdropped)
18975 continue;
18976 type_attname = NameStr(type_attr->attname);
18977
18978 /* Get the next non-dropped table attribute. */
18979 do
18980 {
18981 if (table_attno > tableTupleDesc->natts)
18982 ereport(ERROR,
18984 errmsg("table is missing column \"%s\"",
18985 type_attname)));
18987 table_attno++;
18988 } while (table_attr->attisdropped);
18989 table_attname = NameStr(table_attr->attname);
18990
18991 /* Compare name. */
18993 ereport(ERROR,
18995 errmsg("table has column \"%s\" where type requires \"%s\"",
18997
18998 /* Compare type. */
18999 if (table_attr->atttypid != type_attr->atttypid ||
19000 table_attr->atttypmod != type_attr->atttypmod ||
19001 table_attr->attcollation != type_attr->attcollation)
19002 ereport(ERROR,
19004 errmsg("table \"%s\" has different type for column \"%s\"",
19006 }
19008
19009 /* Any remaining columns at the end of the table had better be dropped. */
19010 for (; table_attno <= tableTupleDesc->natts; table_attno++)
19011 {
19013 table_attno - 1);
19014
19015 if (!table_attr->attisdropped)
19016 ereport(ERROR,
19018 errmsg("table has extra column \"%s\"",
19019 NameStr(table_attr->attname))));
19020 }
19021
19022 /* If the table was already typed, drop the existing dependency. */
19023 if (rel->rd_rel->reloftype)
19024 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
19026
19027 /* Record a dependency on the new type. */
19028 tableobj.classId = RelationRelationId;
19029 tableobj.objectId = relid;
19030 tableobj.objectSubId = 0;
19031 typeobj.classId = TypeRelationId;
19032 typeobj.objectId = typeid;
19033 typeobj.objectSubId = 0;
19035
19036 /* Update pg_class.reloftype */
19040 elog(ERROR, "cache lookup failed for relation %u", relid);
19041 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
19043
19045
19048
19050
19051 return typeobj;
19052}
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:7284
#define ReleaseTupleDesc(tupdesc)
Definition tupdesc.h:240
TupleDesc lookup_rowtype_tupdesc(Oid type_id, int32 typmod)
Definition typcache.c:1949

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

9821{
9822 ObjectAddress address;
9823
9825
9826 /* The CreateStatsStmt has already been through transformStatsStmt */
9827 Assert(stmt->transformed);
9828
9829 address = CreateStatistics(stmt, !is_rebuild);
9830
9831 return address;
9832}
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,
List changing_conids,
LOCKMODE  lockmode 
)
static

Definition at line 12717 of file tablecmds.c.

12722{
12724 Relation rel;
12725 bool changed = false;
12726 List *children = NIL;
12727 bool target_enforced = cmdcon->is_enforced;
12729
12730 /* Since this function recurses, it could be driven to stack overflow */
12732
12733 Assert(cmdcon->alterEnforceability);
12734
12736
12737 Assert(currcon->contype == CONSTRAINT_CHECK);
12738
12739 /*
12740 * Parent relation already locked by caller, children will be locked by
12741 * find_all_inheritors. So NoLock is fine here.
12742 */
12743 rel = table_open(currcon->conrelid, NoLock);
12744
12745 /*
12746 * When setting a constraint to NOT ENFORCED, check whether any matching
12747 * parent constraint remains ENFORCED and is not part of this ALTER.
12748 *
12749 * For a direct ALTER of an inherited constraint, reject the command,
12750 * because the child cannot be weakened while its parent remains enforced.
12751 *
12752 * During recursion, another parent outside this ALTER may still enforce
12753 * the same constraint in a regular inheritance hierarchy. In that case,
12754 * keep the child constraint ENFORCED so that its merged enforceability
12755 * still reflects the remaining enforced parent. Partitions do not need
12756 * this recursive parent check because a partition can have only one
12757 * direct parent.
12758 */
12759 if (!cmdcon->is_enforced &&
12760 (!recursing || !rel->rd_rel->relispartition) &&
12764 {
12765 if (!recursing)
12766 ereport(ERROR,
12768 errmsg("cannot mark inherited constraint \"%s\" as %s",
12769 NameStr(currcon->conname),
12770 "NOT ENFORCED"),
12771 errdetail("The matching constraint on parent table \"%s\" is %s.",
12772 get_rel_name(enforced_parentoid), "ENFORCED"));
12773
12774 target_enforced = true;
12775 }
12776
12777 /*
12778 * Update to the merged enforceability if needed. This may differ from the
12779 * requested enforceability when another matching parent constraint
12780 * remains enforced.
12781 */
12782 if (currcon->conenforced != target_enforced)
12783 {
12785
12788 changed = true;
12789 }
12790
12791 /*
12792 * Note that we must recurse even when trying to change a check constraint
12793 * to not enforced if it is already not enforced, in case descendant
12794 * constraints might be enforced and need to be changed to not enforced,
12795 * unless they still inherit an enforced constraint from another parent.
12796 * Conversely, we should do nothing if a constraint is being set to
12797 * enforced and is already enforced, as descendant constraints cannot be
12798 * different in that case.
12799 */
12800 if (!cmdcon->is_enforced || changed)
12801 {
12802 /*
12803 * If we're recursing, the parent has already done this, so skip it.
12804 * Also, if the constraint is a NO INHERIT constraint, we shouldn't
12805 * try to look for it in the children.
12806 */
12807 if (!recursing && !currcon->connoinherit)
12808 {
12810
12811 children = find_all_inheritors(RelationGetRelid(rel),
12812 lockmode, NULL);
12813
12814 /*
12815 * When setting NOT ENFORCED, build the set of equivalent CHECK
12816 * constraints that this command will attempt to change before
12817 * visiting descendants. The root itself has already been checked
12818 * above.
12819 */
12820 if (!cmdcon->is_enforced)
12822
12823 foreach_oid(childoid, children)
12824 {
12825 if (childoid == RelationGetRelid(rel))
12826 continue;
12827
12828 /*
12829 * If we are told not to recurse, there had better not be any
12830 * child tables, because we can't change constraint
12831 * enforceability on the parent unless we have changed
12832 * enforceability for all child.
12833 */
12834 if (!recurse)
12835 ereport(ERROR,
12837 errmsg("constraint must be altered on child tables too"),
12838 errhint("Do not specify the ONLY keyword."));
12839
12840 /*
12841 * It is sufficient to look up the constraint by name here.
12842 * Supported DDL ensures that inheritable CHECK constraints
12843 * with the same name have equivalent definitions when they
12844 * are propagated to children or when inheritance is
12845 * established. All descendants returned by
12846 * find_all_inheritors must have this constraint: inherited
12847 * CHECK constraints propagate to all children at
12848 * inheritance-link creation time and cannot be dropped
12849 * independently on child tables.
12850 */
12851 if (!cmdcon->is_enforced)
12855 cmdcon->conname,
12856 false));
12857 }
12858 }
12859
12860 foreach_oid(childoid, children)
12861 {
12862 if (childoid == RelationGetRelid(rel))
12863 continue;
12864
12866 childoid, false, true,
12868 lockmode);
12869 }
12870 }
12871
12872 /*
12873 * Tell Phase 3 to check that the constraint is satisfied by existing
12874 * rows. We only need do this when altering the constraint from NOT
12875 * ENFORCED to ENFORCED.
12876 */
12877 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12878 !currcon->conenforced &&
12880 {
12881 AlteredTableInfo *tab;
12883 Datum val;
12884 char *conbin;
12885
12887 newcon->name = pstrdup(NameStr(currcon->conname));
12888 newcon->contype = CONSTR_CHECK;
12889
12894
12895 /* Find or create work queue entry for this table */
12896 tab = ATGetQueueEntry(wqueue, rel);
12897 tab->constraints = lappend(tab->constraints, newcon);
12898 }
12899
12900 table_close(rel, NoLock);
12901
12902 return changed;
12903}
#define TextDatumGetCString(d)
Definition builtins.h:99
long val
Definition informix.c:689
Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok)
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 AlterCheckConstrEnforceabilityRecurse(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, Oid conrelid, bool recurse, bool recursing, List *changing_conids, LOCKMODE lockmode)
static void AlterConstrUpdateConstraintEntry(ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple)

References AlterCheckConstrEnforceabilityRecurse(), AlterConstrUpdateConstraintEntry(), Assert, ATCheckCheckConstrHasEnforcedParent(), ATGetQueueEntry(), check_stack_depth(), CONSTR_CHECK, AlteredTableInfo::constraints, ereport, errcode(), errdetail(), errhint(), errmsg, ERROR, expand_generated_columns_in_expr(), fb(), find_all_inheritors(), foreach_oid, Form_pg_constraint, get_rel_name(), get_relation_constraint_oid(), GETSTRUCT(), InvalidOid, ATAlterConstraint::is_enforced, lappend(), list_append_unique_oid(), list_make1_oid, 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 16660 of file tablecmds.c.

16664{
16667 ForeignServer *server;
16669 HeapTuple tuple;
16670 HeapTuple newtuple;
16671 bool isnull;
16675 Datum datum;
16679 ObjectAddress address;
16680
16681 if (options == NIL)
16682 return InvalidObjectAddress;
16683
16684 /* First, determine FDW validator associated to the foreign table. */
16687 if (!HeapTupleIsValid(tuple))
16688 ereport(ERROR,
16690 errmsg("foreign table \"%s\" does not exist",
16693 server = GetForeignServer(fttableform->ftserver);
16694 fdw = GetForeignDataWrapper(server->fdwid);
16695
16697 ReleaseSysCache(tuple);
16698
16701 if (!HeapTupleIsValid(tuple))
16702 ereport(ERROR,
16704 errmsg("column \"%s\" of relation \"%s\" does not exist",
16706
16707 /* Prevent them from altering a system attribute */
16709 attnum = atttableform->attnum;
16710 if (attnum <= 0)
16711 ereport(ERROR,
16713 errmsg("cannot alter system column \"%s\"", colName)));
16714
16715
16716 /* Initialize buffers for new tuple values */
16717 memset(repl_val, 0, sizeof(repl_val));
16718 memset(repl_null, false, sizeof(repl_null));
16719 memset(repl_repl, false, sizeof(repl_repl));
16720
16721 /* Extract the current options */
16722 datum = SysCacheGetAttr(ATTNAME,
16723 tuple,
16725 &isnull);
16726 if (isnull)
16727 datum = PointerGetDatum(NULL);
16728
16729 /* Transform the options */
16731 datum,
16732 options,
16733 fdw->fdwvalidator);
16734
16735 if (DatumGetPointer(datum) != NULL)
16737 else
16739
16741
16742 /* Everything looks good - update the tuple */
16743
16744 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16746
16747 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16748
16750 RelationGetRelid(rel),
16751 atttableform->attnum);
16753 RelationGetRelid(rel), attnum);
16754
16755 ReleaseSysCache(tuple);
16756
16758
16759 heap_freetuple(newtuple);
16760
16761 return address;
16762}
ForeignDataWrapper * GetForeignDataWrapper(Oid fdwid)
Definition foreign.c:39
ForeignServer * GetForeignServer(Oid serverid)
Definition foreign.c:114
Datum transformGenericOptions(Oid catalogId, Datum oldOptions, List *options, Oid fdwvalidator)
HeapTuple heap_modify_tuple(HeapTuple tuple, TupleDesc tupleDesc, const Datum *replValues, const bool *replIsnull, const bool *doReplace)
Definition heaptuple.c:1118
END_CATALOG_STRUCT typedef FormData_pg_foreign_table * Form_pg_foreign_table
static Pointer DatumGetPointer(Datum X)
Definition postgres.h:332
#define PointerGetDatum(X)
Definition postgres.h:354
Oid rd_id
Definition rel.h:113
HeapTuple SearchSysCacheAttName(Oid relid, const char *attname)
Definition syscache.c:476
Datum SysCacheGetAttr(SysCacheIdentifier cacheId, HeapTuple tup, AttrNumber attributeNumber, bool *isNull)
Definition syscache.c:596

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

Referenced by ATExecCmd().

◆ ATExecAlterColumnType()

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

Definition at line 15263 of file tablecmds.c.

15265{
15266 char *colName = cmd->name;
15267 ColumnDef *def = (ColumnDef *) cmd->def;
15268 TypeName *typeName = def->typeName;
15271 attOldTup;
15275 Oid targettype;
15276 int32 targettypmod;
15281 ScanKeyData key[3];
15282 SysScanDesc scan;
15284 ObjectAddress address;
15285
15286 /*
15287 * Clear all the missing values if we're rewriting the table, since this
15288 * renders them pointless.
15289 */
15290 if (tab->rewrite)
15291 {
15293
15297 /* make sure we don't conflict with later attribute modifications */
15299 }
15300
15302
15303 /* Look up the target column */
15305 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
15306 ereport(ERROR,
15308 errmsg("column \"%s\" of relation \"%s\" does not exist",
15311 attnum = attTup->attnum;
15313
15314 /* Check for multiple ALTER TYPE on same column --- can't cope */
15315 if (attTup->atttypid != attOldTup->atttypid ||
15316 attTup->atttypmod != attOldTup->atttypmod)
15317 ereport(ERROR,
15319 errmsg("cannot alter type of column \"%s\" twice",
15320 colName)));
15321
15322 /* Look up the target type (should not fail, since prep found it) */
15323 typeTuple = typenameType(NULL, typeName, &targettypmod);
15325 targettype = tform->oid;
15326 /* And the collation */
15327 targetcollid = GetColumnDefCollation(NULL, def, targettype);
15328
15329 /*
15330 * If there is a default expression for the column, get it and ensure we
15331 * can coerce it to the new datatype. (We must do this before changing
15332 * the column type, because build_column_default itself will try to
15333 * coerce, and will not issue the error message we want if it fails.)
15334 *
15335 * We remove any implicit coercion steps at the top level of the old
15336 * default expression; this has been agreed to satisfy the principle of
15337 * least surprise. (The conversion to the new column type should act like
15338 * it started from what the user sees as the stored expression, and the
15339 * implicit coercions aren't going to be shown.)
15340 */
15341 if (attTup->atthasdef)
15342 {
15346 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
15348 targettype, targettypmod,
15351 -1);
15352 if (defaultexpr == NULL)
15353 {
15354 if (attTup->attgenerated)
15355 ereport(ERROR,
15357 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
15358 colName, format_type_be(targettype))));
15359 else
15360 ereport(ERROR,
15362 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
15363 colName, format_type_be(targettype))));
15364 }
15365 }
15366 else
15367 defaultexpr = NULL;
15368
15369 /*
15370 * Find everything that depends on the column (constraints, indexes, etc),
15371 * and record enough information to let us recreate the objects.
15372 *
15373 * The actual recreation does not happen here, but only after we have
15374 * performed all the individual ALTER TYPE operations. We have to save
15375 * the info before executing ALTER TYPE, though, else the deparser will
15376 * get confused.
15377 */
15379
15380 /*
15381 * Now scan for dependencies of this column on other things. The only
15382 * things we should find are the dependency on the column datatype and
15383 * possibly a collation dependency. Those can be removed.
15384 */
15386
15387 ScanKeyInit(&key[0],
15391 ScanKeyInit(&key[1],
15395 ScanKeyInit(&key[2],
15399
15401 NULL, 3, key);
15402
15404 {
15407
15408 foundObject.classId = foundDep->refclassid;
15409 foundObject.objectId = foundDep->refobjid;
15410 foundObject.objectSubId = foundDep->refobjsubid;
15411
15412 if (foundDep->deptype != DEPENDENCY_NORMAL)
15413 elog(ERROR, "found unexpected dependency type '%c'",
15414 foundDep->deptype);
15415 if (!(foundDep->refclassid == TypeRelationId &&
15416 foundDep->refobjid == attTup->atttypid) &&
15417 !(foundDep->refclassid == CollationRelationId &&
15418 foundDep->refobjid == attTup->attcollation))
15419 elog(ERROR, "found unexpected dependency for column: %s",
15421
15422 CatalogTupleDelete(depRel, &depTup->t_self);
15423 }
15424
15425 systable_endscan(scan);
15426
15428
15429 /*
15430 * Here we go --- change the recorded column type and collation. (Note
15431 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
15432 * fix up the missing value if any.
15433 */
15434 if (attTup->atthasmissing)
15435 {
15437 bool missingNull;
15438
15439 /* if rewrite is true the missing value should already be cleared */
15440 Assert(tab->rewrite == 0);
15441
15442 /* Get the missing value datum */
15445 attrelation->rd_att,
15446 &missingNull);
15447
15448 /* if it's a null array there is nothing to do */
15449
15450 if (!missingNull)
15451 {
15452 /*
15453 * Get the datum out of the array and repack it in a new array
15454 * built with the new type data. We assume that since the table
15455 * doesn't need rewriting, the actual Datum doesn't need to be
15456 * changed, only the array metadata.
15457 */
15458
15459 int one = 1;
15460 bool isNull;
15462 bool nullsAtt[Natts_pg_attribute] = {0};
15463 bool replacesAtt[Natts_pg_attribute] = {0};
15465
15467 1,
15468 &one,
15469 0,
15470 attTup->attlen,
15471 attTup->attbyval,
15472 attTup->attalign,
15473 &isNull);
15475 1,
15476 targettype,
15477 tform->typlen,
15478 tform->typbyval,
15479 tform->typalign));
15480
15484
15488 heapTup = newTup;
15490 }
15491 }
15492
15493 attTup->atttypid = targettype;
15494 attTup->atttypmod = targettypmod;
15495 attTup->attcollation = targetcollid;
15496 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
15497 ereport(ERROR,
15499 errmsg("too many array dimensions"));
15500 attTup->attndims = list_length(typeName->arrayBounds);
15501 attTup->attlen = tform->typlen;
15502 attTup->attbyval = tform->typbyval;
15503 attTup->attalign = tform->typalign;
15504 attTup->attstorage = tform->typstorage;
15505 attTup->attcompression = InvalidCompressionMethod;
15506
15508
15510
15512
15513 /* Install dependencies on new datatype and collation */
15516
15517 /*
15518 * Drop any pg_statistic entry for the column, since it's now wrong type
15519 */
15521
15523 RelationGetRelid(rel), attnum);
15524
15525 /*
15526 * Update the default, if present, by brute force --- remove and re-add
15527 * the default. Probably unsafe to take shortcuts, since the new version
15528 * may well have additional dependencies. (It's okay to do this now,
15529 * rather than after other ALTER TYPE commands, since the default won't
15530 * depend on other column types.)
15531 */
15532 if (defaultexpr)
15533 {
15534 /*
15535 * If it's a GENERATED default, drop its dependency records, in
15536 * particular its INTERNAL dependency on the column, which would
15537 * otherwise cause dependency.c to refuse to perform the deletion.
15538 */
15539 if (attTup->attgenerated)
15540 {
15542
15543 if (!OidIsValid(attrdefoid))
15544 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15545 RelationGetRelid(rel), attnum);
15547 }
15548
15549 /*
15550 * Make updates-so-far visible, particularly the new pg_attribute row
15551 * which will be updated again.
15552 */
15554
15555 /*
15556 * We use RESTRICT here for safety, but at present we do not expect
15557 * anything to depend on the default.
15558 */
15560 true);
15561
15562 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15563 }
15564
15566 RelationGetRelid(rel), attnum);
15567
15568 /* Cleanup */
15570
15571 return address;
15572}
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:729
void RelationClearMissing(Relation rel)
Definition heap.c:1984
void RemoveStatistics(Oid relid, AttrNumber attnum)
Definition heap.c:3515
static Datum heap_getattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, bool *isnull)
void CatalogTupleDelete(Relation heapRel, const ItemPointerData *tid)
Definition indexing.c:365
Oid exprType(const Node *expr)
Definition nodeFuncs.c:42
Node * strip_implicit_coercions(Node *node)
Definition nodeFuncs.c:710
char * getObjectDescription(const ObjectAddress *object, bool missing_ok)
@ DROP_RESTRICT
Oid StoreAttrDefault(Relation rel, AttrNumber attnum, Node *expr, bool is_internal)
Definition pg_attrdef.c:37
Oid GetAttrDefaultOid(Oid relid, AttrNumber attnum)
Definition pg_attrdef.c:280
void RemoveAttrDefault(Oid relid, AttrNumber attnum, DropBehavior behavior, bool complain, bool internal)
Definition pg_attrdef.c:154
long deleteDependencyRecordsFor(Oid classId, Oid objectId, bool skipExtensionDeps)
Definition pg_depend.c:314
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
List * arrayBounds
Definition parsenodes.h:295
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 12346 of file tablecmds.c.

12348{
12351 SysScanDesc scan;
12352 ScanKeyData skey[3];
12355 ObjectAddress address;
12356
12357 /*
12358 * Disallow altering ONLY a partitioned table, as it would make no sense.
12359 * This is okay for legacy inheritance.
12360 */
12361 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
12362 ereport(ERROR,
12364 errmsg("constraint must be altered in child tables too"),
12365 errhint("Do not specify the ONLY keyword."));
12366
12367
12370
12371 /*
12372 * Find and check the target constraint
12373 */
12374 ScanKeyInit(&skey[0],
12378 ScanKeyInit(&skey[1],
12382 ScanKeyInit(&skey[2],
12385 CStringGetDatum(cmdcon->conname));
12387 true, NULL, 3, skey);
12388
12389 /* There can be at most one matching row */
12391 ereport(ERROR,
12393 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12394 cmdcon->conname, RelationGetRelationName(rel))));
12395
12397 if (cmdcon->alterDeferrability && currcon->contype != CONSTRAINT_FOREIGN)
12398 ereport(ERROR,
12400 errmsg("constraint \"%s\" of relation \"%s\" is not a foreign key constraint",
12401 cmdcon->conname, RelationGetRelationName(rel))));
12402 if (cmdcon->alterEnforceability &&
12403 (currcon->contype != CONSTRAINT_FOREIGN && currcon->contype != CONSTRAINT_CHECK))
12404 ereport(ERROR,
12406 errmsg("cannot alter enforceability of constraint \"%s\" of relation \"%s\"",
12407 cmdcon->conname, RelationGetRelationName(rel)),
12408 errhint("Only foreign key and check constraints can change enforceability.")));
12409 if (cmdcon->alterInheritability &&
12410 currcon->contype != CONSTRAINT_NOTNULL)
12411 ereport(ERROR,
12413 errmsg("constraint \"%s\" of relation \"%s\" is not a not-null constraint",
12414 cmdcon->conname, RelationGetRelationName(rel)));
12415 if (cmdcon->alterInheritability &&
12416 cmdcon->noinherit && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
12417 ereport(ERROR,
12419 errmsg("not-null constraint \"%s\" on partitioned table \"%s\" cannot be NO INHERIT",
12420 cmdcon->conname, RelationGetRelationName(rel)));
12421
12422 /* Refuse to modify inheritability of inherited constraints */
12423 if (cmdcon->alterInheritability &&
12424 cmdcon->noinherit && currcon->coninhcount > 0)
12425 ereport(ERROR,
12427 errmsg("cannot alter inherited constraint \"%s\" on relation \"%s\"",
12428 NameStr(currcon->conname),
12430
12431 /*
12432 * If it's not the topmost constraint, raise an error.
12433 *
12434 * Altering a non-topmost constraint leaves some triggers untouched, since
12435 * they are not directly connected to this constraint; also, pg_dump would
12436 * ignore the deferrability status of the individual constraint, since it
12437 * only dumps topmost constraints. Avoid these problems by refusing this
12438 * operation and telling the user to alter the parent constraint instead.
12439 */
12440 if (OidIsValid(currcon->conparentid))
12441 {
12442 HeapTuple tp;
12443 Oid parent = currcon->conparentid;
12444 char *ancestorname = NULL;
12445 char *ancestortable = NULL;
12446
12447 /* Loop to find the topmost constraint */
12449 {
12451
12452 /* If no parent, this is the constraint we want */
12453 if (!OidIsValid(contup->conparentid))
12454 {
12455 ancestorname = pstrdup(NameStr(contup->conname));
12456 ancestortable = get_rel_name(contup->conrelid);
12457 ReleaseSysCache(tp);
12458 break;
12459 }
12460
12461 parent = contup->conparentid;
12462 ReleaseSysCache(tp);
12463 }
12464
12465 ereport(ERROR,
12467 errmsg("cannot alter constraint \"%s\" on relation \"%s\"",
12468 cmdcon->conname, RelationGetRelationName(rel)),
12470 errdetail("Constraint \"%s\" is derived from constraint \"%s\" of relation \"%s\".",
12471 cmdcon->conname, ancestorname, ancestortable) : 0,
12472 errhint("You may alter the constraint it derives from instead.")));
12473 }
12474
12475 address = InvalidObjectAddress;
12476
12477 /*
12478 * Do the actual catalog work, and recurse if necessary.
12479 */
12481 contuple, recurse, lockmode))
12483
12484 systable_endscan(scan);
12485
12488
12489 return address;
12490}
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 12497 of file tablecmds.c.

12501{
12503 bool changed = false;
12504 List *otherrelids = NIL;
12505
12507
12508 /*
12509 * Do the catalog work for the enforceability or deferrability change,
12510 * recurse if necessary.
12511 *
12512 * Note that even if deferrability is requested to be altered along with
12513 * enforceability, we don't need to explicitly update multiple entries in
12514 * pg_trigger related to deferrability.
12515 *
12516 * Modifying foreign key enforceability involves either creating or
12517 * dropping the trigger, during which the deferrability setting will be
12518 * adjusted automatically.
12519 */
12520 if (cmdcon->alterEnforceability)
12521 {
12522 if (currcon->contype == CONSTRAINT_FOREIGN)
12524 currcon->conrelid,
12525 currcon->confrelid,
12526 contuple, lockmode,
12529 else if (currcon->contype == CONSTRAINT_CHECK)
12531 contuple, recurse, false,
12532 NIL, lockmode);
12533 }
12534 else if (cmdcon->alterDeferrability &&
12536 contuple, recurse, &otherrelids,
12537 lockmode))
12538 {
12539 /*
12540 * AlterConstrUpdateConstraintEntry already invalidated relcache for
12541 * the relations having the constraint itself; here we also invalidate
12542 * for relations that have any triggers that are part of the
12543 * constraint.
12544 */
12545 foreach_oid(relid, otherrelids)
12547
12548 changed = true;
12549 }
12550
12551 /*
12552 * Do the catalog work for the inheritability change.
12553 */
12554 if (cmdcon->alterInheritability &&
12556 lockmode))
12557 changed = true;
12558
12559 return changed;
12560}
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 13092 of file tablecmds.c.

13096{
13098 Oid refrelid;
13099 bool changed = false;
13100
13101 /* since this function recurses, it could be driven to stack overflow */
13103
13104 Assert(cmdcon->alterDeferrability);
13105
13107 refrelid = currcon->confrelid;
13108
13109 /* Should be foreign key constraint */
13110 Assert(currcon->contype == CONSTRAINT_FOREIGN);
13111
13112 /*
13113 * If called to modify a constraint that's already in the desired state,
13114 * silently do nothing.
13115 */
13116 if (currcon->condeferrable != cmdcon->deferrable ||
13117 currcon->condeferred != cmdcon->initdeferred)
13118 {
13120 changed = true;
13121
13122 /*
13123 * Now we need to update the multiple entries in pg_trigger that
13124 * implement the constraint.
13125 */
13127 cmdcon->deferrable,
13128 cmdcon->initdeferred, otherrelids);
13129 }
13130
13131 /*
13132 * If the table at either end of the constraint is partitioned, we need to
13133 * handle every constraint that is a child of this one.
13134 */
13135 if (recurse && changed &&
13136 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13139 contuple, recurse, otherrelids,
13140 lockmode);
13141
13142 return changed;
13143}
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 13149 of file tablecmds.c.

13152{
13155 char *colName;
13156 List *children;
13157
13158 Assert(cmdcon->alterInheritability);
13159
13161
13162 /* The current implementation only works for NOT NULL constraints */
13163 Assert(currcon->contype == CONSTRAINT_NOTNULL);
13164
13165 /*
13166 * If called to modify a constraint that's already in the desired state,
13167 * silently do nothing.
13168 */
13169 if (cmdcon->noinherit == currcon->connoinherit)
13170 return false;
13171
13174
13175 /* Fetch the column number and name */
13177 colName = get_attname(currcon->conrelid, colNum, false);
13178
13179 /*
13180 * Propagate the change to children. For this subcommand type we don't
13181 * recursively affect children, just the immediate level.
13182 */
13184 lockmode);
13185 foreach_oid(childoid, children)
13186 {
13187 ObjectAddress addr;
13188
13189 if (cmdcon->noinherit)
13190 {
13193
13195 if (!childtup)
13196 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13197 colName, childoid);
13199 Assert(childcon->coninhcount > 0);
13200 childcon->coninhcount--;
13201 childcon->conislocal = true;
13204 }
13205 else
13206 {
13208
13209 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
13210 colName, true, true, lockmode);
13211 if (OidIsValid(addr.objectId))
13214 }
13215 }
13216
13217 return true;
13218}
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition lsyscache.c:1053
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:8054

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

12583{
12585 Oid conoid;
12586 Relation rel;
12587 bool changed = false;
12588
12589 /* Since this function recurses, it could be driven to stack overflow */
12591
12592 Assert(cmdcon->alterEnforceability);
12593
12595 conoid = currcon->oid;
12596
12597 /* Should be foreign key constraint */
12598 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12599
12600 rel = table_open(currcon->conrelid, lockmode);
12601
12602 if (currcon->conenforced != cmdcon->is_enforced)
12603 {
12605 changed = true;
12606 }
12607
12608 /* Drop triggers */
12609 if (!cmdcon->is_enforced)
12610 {
12611 /*
12612 * When setting a constraint to NOT ENFORCED, the constraint triggers
12613 * need to be dropped. Therefore, we must process the child relations
12614 * first, followed by the parent, to account for dependencies.
12615 */
12616 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12620 lockmode, InvalidOid, InvalidOid,
12622
12623 /* Drop all the triggers */
12625 }
12626 else if (changed) /* Create triggers */
12627 {
12632
12633 /* Prepare the minimal information required for trigger creation. */
12635
12636 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12637 fkconstraint->fk_matchtype = currcon->confmatchtype;
12638 fkconstraint->fk_upd_action = currcon->confupdtype;
12639 fkconstraint->fk_del_action = currcon->confdeltype;
12640 fkconstraint->deferrable = currcon->condeferrable;
12641 fkconstraint->initdeferred = currcon->condeferred;
12642
12643 /* Create referenced triggers */
12644 if (currcon->conrelid == fkrelid)
12646 currcon->confrelid,
12648 conoid,
12649 currcon->conindid,
12654
12655 /* Create referencing triggers */
12656 if (currcon->confrelid == pkrelid)
12658 pkrelid,
12660 conoid,
12661 currcon->conindid,
12666
12667 /*
12668 * Tell Phase 3 to check that the constraint is satisfied by existing
12669 * rows. Only applies to leaf partitions, and (for constraints that
12670 * reference a partitioned table) only if this is not one of the
12671 * pg_constraint rows that exist solely to support action triggers.
12672 */
12673 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12674 currcon->confrelid == pkrelid)
12675 {
12676 AlteredTableInfo *tab;
12678
12680 newcon->name = fkconstraint->conname;
12681 newcon->contype = CONSTR_FOREIGN;
12682 newcon->refrelid = currcon->confrelid;
12683 newcon->refindid = currcon->conindid;
12684 newcon->conid = currcon->oid;
12685 newcon->qual = (Node *) fkconstraint;
12686
12687 /* Find or create work queue entry for this table */
12688 tab = ATGetQueueEntry(wqueue, rel);
12689 tab->constraints = lappend(tab->constraints, newcon);
12690 }
12691
12692 /*
12693 * If the table at either end of the constraint is partitioned, we
12694 * need to recurse and create triggers for each constraint that is a
12695 * child of this one.
12696 */
12697 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12701 lockmode,
12706 }
12707
12708 table_close(rel, NoLock);
12709
12710 return changed;
12711}
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 21016 of file tablecmds.c.

21018{
21020 catalog;
21023 SysScanDesc scan;
21025 AttrNumber attno;
21026 int natts;
21028 ObjectAddress address;
21029 const char *trigger_name;
21033 ParseState *pstate = make_parsestate(NULL);
21034
21035 pstate->p_sourcetext = context->queryString;
21036
21037 /*
21038 * We must lock the default partition if one exists, because attaching a
21039 * new partition will change its partition constraint.
21040 */
21045
21047
21048 /*
21049 * XXX I think it'd be a good idea to grab locks on all tables referenced
21050 * by FKs at this point also.
21051 */
21052
21053 /*
21054 * Must be owner of both parent and source table -- parent was checked by
21055 * ATSimplePermissions call in ATPrepCmd
21056 */
21059
21060 /* A partition can only have one parent */
21061 if (attachrel->rd_rel->relispartition)
21062 ereport(ERROR,
21064 errmsg("\"%s\" is already a partition",
21066
21067 if (OidIsValid(attachrel->rd_rel->reloftype))
21068 ereport(ERROR,
21070 errmsg("cannot attach a typed table as partition")));
21071
21072 /*
21073 * Disallow attaching a partition if the table is referenced in a
21074 * publication EXCEPT clause. Changing the partition hierarchy could alter
21075 * the effective publication membership.
21076 */
21078 if (exceptpuboids != NIL)
21079 {
21080 bool first = true;
21082
21084
21086 {
21087 char *pubname = get_publication_name(pubid, false);
21088
21089 if (first)
21090 appendStringInfo(&pubnames, _("\"%s\""), pubname);
21091 else
21092 appendStringInfo(&pubnames, _(", \"%s\""), pubname);
21093 first = false;
21094 }
21095
21096 ereport(ERROR,
21098 errmsg_plural("cannot attach table \"%s\" as partition because it is referenced in publication %s EXCEPT clause",
21099 "cannot attach table \"%s\" as partition because it is referenced in publications %s EXCEPT clause",
21102 pubnames.data),
21103 errdetail("The publication EXCEPT clause cannot contain tables that are partitions."),
21104 errhint("Change the publication's EXCEPT clause using ALTER PUBLICATION ... SET ALL TABLES."));
21105 }
21106
21108
21109 /*
21110 * Table being attached should not already be part of inheritance; either
21111 * as a child table...
21112 */
21119 NULL, 1, &skey);
21121 ereport(ERROR,
21123 errmsg("cannot attach inheritance child as partition")));
21124 systable_endscan(scan);
21125
21126 /* ...or as a parent table (except the case when it is partitioned) */
21132 1, &skey);
21134 attachrel->rd_rel->relkind == RELKIND_RELATION)
21135 ereport(ERROR,
21137 errmsg("cannot attach inheritance parent as partition")));
21138 systable_endscan(scan);
21140
21141 /*
21142 * Prevent circularity by seeing if rel is a partition of attachrel. (In
21143 * particular, this disallows making a rel a partition of itself.)
21144 *
21145 * We do that by checking if rel is a member of the list of attachrel's
21146 * partitions provided the latter is partitioned at all. We want to avoid
21147 * having to construct this list again, so we request the strongest lock
21148 * on all partitions. We need the strongest lock, because we may decide
21149 * to scan them if we find out that the table being attached (or its leaf
21150 * partitions) may contain rows that violate the partition constraint. If
21151 * the table has a constraint that would prevent such rows, which by
21152 * definition is present in all the partitions, we need not scan the
21153 * table, nor its partitions. But we cannot risk a deadlock by taking a
21154 * weaker lock now and the stronger one only when needed.
21155 */
21159 ereport(ERROR,
21161 errmsg("circular inheritance not allowed"),
21162 errdetail("\"%s\" is already a child of \"%s\".",
21165
21166 /* If the parent is permanent, so must be all of its partitions. */
21167 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
21168 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
21169 ereport(ERROR,
21171 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
21173
21174 /* Temp parent cannot have a partition that is itself not a temp */
21175 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
21176 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
21177 ereport(ERROR,
21179 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
21181
21182 /* If the parent is temp, it must belong to this session */
21183 if (RELATION_IS_OTHER_TEMP(rel))
21184 ereport(ERROR,
21186 errmsg("cannot attach as partition of temporary relation of another session")));
21187
21188 /* Ditto for the partition */
21190 ereport(ERROR,
21192 errmsg("cannot attach temporary relation of another session as partition")));
21193
21194 /*
21195 * Check if attachrel has any identity columns or any columns that aren't
21196 * in the parent.
21197 */
21199 natts = tupleDesc->natts;
21200 for (attno = 1; attno <= natts; attno++)
21201 {
21203 char *attributeName = NameStr(attribute->attname);
21204
21205 /* Ignore dropped */
21206 if (attribute->attisdropped)
21207 continue;
21208
21209 if (attribute->attidentity)
21210 ereport(ERROR,
21212 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
21214 errdetail("The new partition may not contain an identity column."));
21215
21216 /* Try to find the column in parent (matching on column name) */
21220 ereport(ERROR,
21222 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
21225 errdetail("The new partition may contain only the columns present in parent.")));
21226 }
21227
21228 /*
21229 * If child_rel has row-level triggers with transition tables, we
21230 * currently don't allow it to become a partition. See also prohibitions
21231 * in ATExecAddInherit() and CreateTrigger().
21232 */
21234 if (trigger_name != NULL)
21235 ereport(ERROR,
21237 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
21239 errdetail("ROW triggers with transition tables are not supported on partitions.")));
21240
21241 /*
21242 * Check that the new partition's bound is valid and does not overlap any
21243 * of existing partitions of the parent - note that it does not return on
21244 * error.
21245 */
21247 cmd->bound, pstate);
21248
21250
21251 /*
21252 * Generate a partition constraint from the partition bound specification.
21253 * If the parent itself is a partition, make sure to include its
21254 * constraint as well.
21255 */
21257
21258 /*
21259 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
21260 * since it's needed later to construct the constraint expression for
21261 * validating against the default partition, if any.
21262 */
21265
21266 /* Skip validation if there are no constraints to validate. */
21267 if (partConstraint)
21268 {
21269 /*
21270 * Run the partition quals through const-simplification similar to
21271 * check constraints. We skip canonicalize_qual, though, because
21272 * partition quals should be in canonical form already.
21273 */
21276 (Node *) partConstraint);
21277
21278 /* XXX this sure looks wrong */
21280
21281 /*
21282 * Adjust the generated constraint to match this partition's attribute
21283 * numbers.
21284 */
21286 rel);
21287
21288 /* Validate partition constraints against the table being attached. */
21290 false);
21291 }
21292
21293 /*
21294 * If we're attaching a partition other than the default partition and a
21295 * default one exists, then that partition's partition constraint changes,
21296 * so add an entry to the work queue to validate it, too. (We must not do
21297 * this when the partition being attached is the default one; we already
21298 * did it above!)
21299 */
21301 {
21304
21305 Assert(!cmd->bound->is_default);
21306
21307 /* we already hold a lock on the default partition */
21311
21312 /*
21313 * Map the Vars in the constraint expression from rel's attnos to
21314 * defaultrel's.
21315 */
21318 1, defaultrel, rel);
21320 defPartConstraint, true);
21321
21322 /* keep our lock until commit. */
21324 }
21325
21327
21328 /*
21329 * If the partition we just attached is partitioned itself, invalidate
21330 * relcache for all descendent partitions too to ensure that their
21331 * rd_partcheck expression trees are rebuilt; partitions already locked at
21332 * the beginning of this function.
21333 */
21334 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21335 {
21336 ListCell *l;
21337
21338 foreach(l, attachrel_children)
21339 {
21341 }
21342 }
21343
21344 /* keep our lock until commit */
21346
21347 return address;
21348}
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition clauses.c:2516
#define _(x)
Definition elog.c:96
int int int errmsg_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...) pg_attribute_printf(1
List * list_concat_copy(const List *list1, const List *list2)
Definition list.c:598
char * get_publication_name(Oid pubid, bool missing_ok)
Definition lsyscache.c:4006
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 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(), 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 22367 of file tablecmds.c.

22368{
22371 Relation parentTbl;
22372 ObjectAddress address;
22373 Oid partIdxId;
22376
22377 /*
22378 * We need to obtain lock on the index 'name' to modify it, but we also
22379 * need to read its owning table's tuple descriptor -- so we need to lock
22380 * both. To avoid deadlocks, obtain lock on the table before doing so on
22381 * the index. Furthermore, we need to examine the parent table of the
22382 * partition, so lock that one too.
22383 */
22384 state.partitionOid = InvalidOid;
22385 state.parentTblOid = parentIdx->rd_index->indrelid;
22386 state.lockedParentTbl = false;
22387 partIdxId =
22390 &state);
22391 /* Not there? */
22392 if (!OidIsValid(partIdxId))
22393 ereport(ERROR,
22395 errmsg("index \"%s\" does not exist", name->relname)));
22396
22397 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
22399
22400 /* we already hold locks on both tables, so this is safe: */
22401 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
22402 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
22403
22405
22406 /*
22407 * Check if the index is already attached to the correct parent,
22408 * ultimately attempting one round of validation if already the case.
22409 */
22410 currParent = partIdx->rd_rel->relispartition ?
22412 if (currParent != RelationGetRelid(parentIdx))
22413 {
22416 AttrMap *attmap;
22417 bool found;
22418 int i;
22422
22423 /*
22424 * If this partition already has an index attached, refuse the
22425 * operation.
22426 */
22428
22430 ereport(ERROR,
22432 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22434 RelationGetRelationName(parentIdx)),
22435 errdetail("Index \"%s\" is already attached to another index.",
22437
22438 /* Make sure it indexes a partition of the other index's table */
22439 partDesc = RelationGetPartitionDesc(parentTbl, true);
22440 found = false;
22441 for (i = 0; i < partDesc->nparts; i++)
22442 {
22443 if (partDesc->oids[i] == state.partitionOid)
22444 {
22445 found = true;
22446 break;
22447 }
22448 }
22449 if (!found)
22450 ereport(ERROR,
22452 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22454 RelationGetRelationName(parentIdx)),
22455 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
22457 RelationGetRelationName(parentTbl))));
22458
22459 /* Ensure the indexes are compatible */
22461 parentInfo = BuildIndexInfo(parentIdx);
22463 RelationGetDescr(parentTbl),
22464 false);
22466 partIdx->rd_indcollation,
22467 parentIdx->rd_indcollation,
22468 partIdx->rd_opfamily,
22469 parentIdx->rd_opfamily,
22470 attmap))
22471 ereport(ERROR,
22473 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22475 RelationGetRelationName(parentIdx)),
22476 errdetail("The index definitions do not match.")));
22477
22478 /*
22479 * If there is a constraint in the parent, make sure there is one in
22480 * the child too.
22481 */
22483 RelationGetRelid(parentIdx));
22484
22486 {
22488 partIdxId);
22489 if (!OidIsValid(cldConstrId))
22490 ereport(ERROR,
22492 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22494 RelationGetRelationName(parentIdx)),
22495 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
22496 RelationGetRelationName(parentIdx),
22497 RelationGetRelationName(parentTbl),
22499 }
22500
22501 /*
22502 * If it's a primary key, make sure the columns in the partition are
22503 * NOT NULL.
22504 */
22505 if (parentIdx->rd_index->indisprimary)
22507
22508 /* All good -- do it */
22513
22515
22516 validatePartitionedIndex(parentIdx, parentTbl);
22517 }
22518 else if (!parentIdx->rd_index->indisvalid)
22519 {
22520 /*
22521 * The index is attached, but the parent is still invalid; see if it
22522 * can be validated now.
22523 */
22524 validatePartitionedIndex(parentIdx, parentTbl);
22525 }
22526
22527 relation_close(parentTbl, AccessShareLock);
22528 /* keep these locks till commit */
22531
22532 return address;
22533}
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:4601
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 16777 of file tablecmds.c.

16778{
16781 HeapTuple tuple;
16783
16784 /*
16785 * Get exclusive lock till end of transaction on the target table. Use
16786 * relation_open so that we can work on indexes and sequences.
16787 */
16788 target_rel = relation_open(relationOid, lockmode);
16789
16790 /* Get its pg_class tuple, too */
16792
16793 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16794 if (!HeapTupleIsValid(tuple))
16795 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16797
16798 /* Can we change the ownership of this tuple? */
16799 switch (tuple_class->relkind)
16800 {
16801 case RELKIND_RELATION:
16802 case RELKIND_VIEW:
16803 case RELKIND_MATVIEW:
16806 case RELKIND_PROPGRAPH:
16807 /* ok to change owner */
16808 break;
16809 case RELKIND_INDEX:
16810 if (!recursing)
16811 {
16812 /*
16813 * Because ALTER INDEX OWNER used to be allowed, and in fact
16814 * is generated by old versions of pg_dump, we give a warning
16815 * and do nothing rather than erroring out. Also, to avoid
16816 * unnecessary chatter while restoring those old dumps, say
16817 * nothing at all if the command would be a no-op anyway.
16818 */
16819 if (tuple_class->relowner != newOwnerId)
16822 errmsg("cannot change owner of index \"%s\"",
16823 NameStr(tuple_class->relname)),
16824 errhint("Change the ownership of the index's table instead.")));
16825 /* quick hack to exit via the no-op path */
16826 newOwnerId = tuple_class->relowner;
16827 }
16828 break;
16830 if (recursing)
16831 break;
16832 ereport(ERROR,
16834 errmsg("cannot change owner of index \"%s\"",
16835 NameStr(tuple_class->relname)),
16836 errhint("Change the ownership of the index's table instead.")));
16837 break;
16838 case RELKIND_SEQUENCE:
16839 if (!recursing &&
16840 tuple_class->relowner != newOwnerId)
16841 {
16842 /* if it's an owned sequence, disallow changing it by itself */
16843 Oid tableId;
16844 int32 colId;
16845
16846 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16848 ereport(ERROR,
16850 errmsg("cannot change owner of sequence \"%s\"",
16851 NameStr(tuple_class->relname)),
16852 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16853 NameStr(tuple_class->relname),
16855 }
16856 break;
16858 if (recursing)
16859 break;
16860 ereport(ERROR,
16862 errmsg("\"%s\" is a composite type",
16863 NameStr(tuple_class->relname)),
16864 /* translator: %s is an SQL ALTER command */
16865 errhint("Use %s instead.",
16866 "ALTER TYPE")));
16867 break;
16868 case RELKIND_TOASTVALUE:
16869 if (recursing)
16870 break;
16872 default:
16873 ereport(ERROR,
16875 errmsg("cannot change owner of relation \"%s\"",
16876 NameStr(tuple_class->relname)),
16878 }
16879
16880 /*
16881 * If the new owner is the same as the existing owner, consider the
16882 * command to have succeeded. This is for dump restoration purposes.
16883 */
16884 if (tuple_class->relowner != newOwnerId)
16885 {
16889 Acl *newAcl;
16891 bool isNull;
16892 HeapTuple newtuple;
16893
16894 /* skip permission checks when recursing to index or toast table */
16895 if (!recursing)
16896 {
16897 /* Superusers can always do it */
16898 if (!superuser())
16899 {
16900 Oid namespaceOid = tuple_class->relnamespace;
16902
16903 /* Otherwise, must be owner of the existing object */
16904 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16907
16908 /* Must be able to become new owner */
16910
16911 /* New owner must have CREATE privilege on namespace */
16913 ACL_CREATE);
16914 if (aclresult != ACLCHECK_OK)
16917 }
16918 }
16919
16920 memset(repl_null, false, sizeof(repl_null));
16921 memset(repl_repl, false, sizeof(repl_repl));
16922
16925
16926 /*
16927 * Determine the modified ACL for the new owner. This is only
16928 * necessary when the ACL is non-null.
16929 */
16932 &isNull);
16933 if (!isNull)
16934 {
16936 tuple_class->relowner, newOwnerId);
16937 repl_repl[Anum_pg_class_relacl - 1] = true;
16939 }
16940
16942
16943 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16944
16945 heap_freetuple(newtuple);
16946
16947 /*
16948 * We must similarly update any per-column ACLs to reflect the new
16949 * owner; for neatness reasons that's split out as a subroutine.
16950 */
16951 change_owner_fix_column_acls(relationOid,
16952 tuple_class->relowner,
16953 newOwnerId);
16954
16955 /*
16956 * Update owner dependency reference, if any. A composite type has
16957 * none, because it's tracked for the pg_type entry instead of here;
16958 * indexes and TOAST tables don't have their own entries either.
16959 */
16960 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16961 tuple_class->relkind != RELKIND_INDEX &&
16963 tuple_class->relkind != RELKIND_TOASTVALUE)
16965 newOwnerId);
16966
16967 /*
16968 * Also change the ownership of the table's row type, if it has one
16969 */
16970 if (OidIsValid(tuple_class->reltype))
16972
16973 /*
16974 * If we are operating on a table or materialized view, also change
16975 * the ownership of any indexes and sequences that belong to the
16976 * relation, as well as its toast table (if it has one).
16977 */
16978 if (tuple_class->relkind == RELKIND_RELATION ||
16980 tuple_class->relkind == RELKIND_MATVIEW ||
16981 tuple_class->relkind == RELKIND_TOASTVALUE)
16982 {
16984 ListCell *i;
16985
16986 /* Find all the indexes belonging to this relation */
16988
16989 /* For each index, recursively change its ownership */
16990 foreach(i, index_oid_list)
16991 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16992
16994 }
16995
16996 /* If it has a toast table, recurse to change its ownership */
16997 if (tuple_class->reltoastrelid != InvalidOid)
16999 true, lockmode);
17000
17001 /* If it has dependent sequences, recurse to change them too */
17002 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
17003 }
17004
17006
17007 ReleaseSysCache(tuple);
17010}
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition acl.c:1150
void check_can_set_role(Oid member, Oid role)
Definition acl.c:5374
#define DatumGetAclP(X)
Definition acl.h:120
#define pg_fallthrough
Definition c.h:220
#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:4026

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

17154{
17155 Oid indexOid;
17156 ObjectAddress address;
17157
17158 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
17159
17160 if (!OidIsValid(indexOid))
17161 ereport(ERROR,
17163 errmsg("index \"%s\" for table \"%s\" does not exist",
17165
17166 /* Check index is valid to cluster on */
17167 check_index_is_clusterable(rel, indexOid, lockmode);
17168
17169 /* And do the work */
17170 mark_index_clustered(rel, indexOid, false);
17171
17172 ObjectAddressSet(address,
17173 RelationRelationId, indexOid);
17174
17175 return address;
17176}
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition repack.c:732
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition repack.c:792

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

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

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

8269{
8270 TupleDesc tupdesc = RelationGetDescr(rel);
8272 ObjectAddress address;
8273
8274 /*
8275 * get the number of the attribute
8276 */
8279 ereport(ERROR,
8281 errmsg("column \"%s\" of relation \"%s\" does not exist",
8283
8284 /* Prevent them from altering a system attribute */
8285 if (attnum <= 0)
8286 ereport(ERROR,
8288 errmsg("cannot alter system column \"%s\"",
8289 colName)));
8290
8291 if (TupleDescAttr(tupdesc, attnum - 1)->attidentity)
8292 ereport(ERROR,
8294 errmsg("column \"%s\" of relation \"%s\" is an identity column",
8296 /* translator: %s is an SQL ALTER command */
8297 newDefault ? 0 : errhint("Use %s instead.",
8298 "ALTER TABLE ... ALTER COLUMN ... DROP IDENTITY")));
8299
8300 if (TupleDescAttr(tupdesc, attnum - 1)->attgenerated)
8301 ereport(ERROR,
8303 errmsg("column \"%s\" of relation \"%s\" is a generated column",
8305 newDefault ?
8306 /* translator: %s is an SQL ALTER command */
8307 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... SET EXPRESSION") :
8308 (TupleDescAttr(tupdesc, attnum - 1)->attgenerated == ATTRIBUTE_GENERATED_STORED ?
8309 errhint("Use %s instead.", "ALTER TABLE ... ALTER COLUMN ... DROP EXPRESSION") : 0)));
8310
8311 /*
8312 * Remove any old default for the column. We use RESTRICT here for
8313 * safety, but at present we do not expect anything to depend on the
8314 * default.
8315 *
8316 * We treat removing the existing default as an internal operation when it
8317 * is preparatory to adding a new default, but as a user-initiated
8318 * operation when the user asked for a drop.
8319 */
8321 newDefault != NULL);
8322
8323 if (newDefault)
8324 {
8325 /* SET DEFAULT */
8327
8329 rawEnt->attnum = attnum;
8330 rawEnt->raw_default = newDefault;
8331 rawEnt->generated = '\0';
8332
8333 /*
8334 * This function is intended for CREATE TABLE, so it processes a
8335 * _list_ of defaults, but we just do one.
8336 */
8338 false, true, false, NULL);
8339 }
8340
8342 RelationGetRelid(rel), attnum);
8343 return address;
8344}
#define InvalidAttrNumber
Definition attnum.h:23
AttrNumber get_attnum(Oid relid, const char *attname)
Definition lsyscache.c:1084

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

8354{
8355 ObjectAddress address;
8356
8357 /* We assume no checking is required */
8358
8359 /*
8360 * Remove any old default for the column. We use RESTRICT here for
8361 * safety, but at present we do not expect anything to depend on the
8362 * default. (In ordinary cases, there could not be a default in place
8363 * anyway, but it's possible when combining LIKE with inheritance.)
8364 */
8366 true);
8367
8368 (void) StoreAttrDefault(rel, attnum, newDefault, true);
8369
8371 RelationGetRelid(rel), attnum);
8372 return address;
8373}

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

21701{
21702 Relation partRel;
21703 ObjectAddress address;
21705
21706 /*
21707 * We must lock the default partition, because detaching this partition
21708 * will change its partition constraint.
21709 */
21713 {
21714 /*
21715 * Concurrent detaching when a default partition exists is not
21716 * supported. The main problem is that the default partition
21717 * constraint would change. And there's a definitional problem: what
21718 * should happen to the tuples that are being inserted that belong to
21719 * the partition being detached? Putting them on the partition being
21720 * detached would be wrong, since they'd become "lost" after the
21721 * detaching completes but we cannot put them in the default partition
21722 * either until we alter its partition constraint.
21723 *
21724 * I think we could solve this problem if we effected the constraint
21725 * change before committing the first transaction. But the lock would
21726 * have to remain AEL and it would cause concurrent query planning to
21727 * be blocked, so changing it that way would be even worse.
21728 */
21729 if (concurrent)
21730 ereport(ERROR,
21732 errmsg("cannot detach partitions concurrently when a default partition exists")));
21734 }
21735
21736 /*
21737 * In concurrent mode, the partition is locked with share-update-exclusive
21738 * in the first transaction. This allows concurrent transactions to be
21739 * doing DML to the partition.
21740 */
21741 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
21743
21744 /*
21745 * Check inheritance conditions and either delete the pg_inherits row (in
21746 * non-concurrent mode) or just set the inhdetachpending flag.
21747 */
21748 if (!concurrent)
21749 RemoveInheritance(partRel, rel, false);
21750 else
21751 MarkInheritDetached(partRel, rel);
21752
21753 /*
21754 * Ensure that foreign keys still hold after this detach. This keeps
21755 * locks on the referencing tables, which prevents concurrent transactions
21756 * from adding rows that we wouldn't see. For this to work in concurrent
21757 * mode, it is critical that the partition appears as no longer attached
21758 * for the RI queries as soon as the first transaction commits.
21759 */
21761
21762 /*
21763 * Concurrent mode has to work harder; first we add a new constraint to
21764 * the partition that matches the partition constraint. Then we close our
21765 * existing transaction, and in a new one wait for all processes to catch
21766 * up on the catalog updates we've done so far; at that point we can
21767 * complete the operation.
21768 */
21769 if (concurrent)
21770 {
21771 Oid partrelid,
21773 LOCKTAG tag;
21774 char *parentrelname;
21775 char *partrelname;
21776
21777 /*
21778 * We're almost done now; the only traces that remain are the
21779 * pg_inherits tuple and the partition's relpartbounds. Before we can
21780 * remove those, we need to wait until all transactions that know that
21781 * this is a partition are gone.
21782 */
21783
21784 /*
21785 * Remember relation OIDs to re-acquire them later; and relation names
21786 * too, for error messages if something is dropped in between.
21787 */
21788 partrelid = RelationGetRelid(partRel);
21793 RelationGetRelationName(partRel));
21794
21795 /* Invalidate relcache entries for the parent -- must be before close */
21797
21798 table_close(partRel, NoLock);
21799 table_close(rel, NoLock);
21800 tab->rel = NULL;
21801
21802 /* Make updated catalog entry visible */
21805
21807
21808 /*
21809 * Now wait. This ensures that all queries that were planned
21810 * including the partition are finished before we remove the rest of
21811 * catalog entries. We don't need or indeed want to acquire this
21812 * lock, though -- that would block later queries.
21813 *
21814 * We don't need to concern ourselves with waiting for a lock on the
21815 * partition itself, since we will acquire AccessExclusiveLock below.
21816 */
21819
21820 /*
21821 * Now acquire locks in both relations again. Note they may have been
21822 * removed in the meantime, so care is required.
21823 */
21826
21827 /* If the relations aren't there, something bad happened; bail out */
21828 if (rel == NULL)
21829 {
21830 if (partRel != NULL) /* shouldn't happen */
21831 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21832 partrelname);
21833 ereport(ERROR,
21835 errmsg("partitioned table \"%s\" was removed concurrently",
21836 parentrelname)));
21837 }
21838 if (partRel == NULL)
21839 ereport(ERROR,
21841 errmsg("partition \"%s\" was removed concurrently", partrelname)));
21842
21843 tab->rel = rel;
21844 }
21845
21846 /*
21847 * Detaching the partition might involve TOAST table access, so ensure we
21848 * have a valid snapshot.
21849 */
21851
21852 /* Do the final part of detaching */
21853 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21854
21856
21858
21859 /* keep our lock until commit */
21860 table_close(partRel, NoLock);
21861
21862 return address;
21863}
Oid MyDatabaseId
Definition globals.c:96
void CacheInvalidateRelcache(Relation relation)
Definition inval.c:1632
void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress)
Definition lmgr.c:911
#define SET_LOCKTAG_RELATION(locktag, dboid, reloid)
Definition locktag.h:81
char * MemoryContextStrdup(MemoryContext context, const char *string)
Definition mcxt.c:1897
MemoryContext PortalContext
Definition mcxt.c:176
Snapshot GetTransactionSnapshot(void)
Definition snapmgr.c:272
void PushActiveSnapshot(Snapshot snapshot)
Definition snapmgr.c:682
void PopActiveSnapshot(void)
Definition snapmgr.c:775
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:89
static void MarkInheritDetached(Relation child_rel, Relation parent_rel)
static void RemoveInheritance(Relation child_rel, Relation parent_rel, bool expect_detached)
static void DetachPartitionFinalize(Relation rel, Relation partRel, bool concurrent, Oid defaultPartOid)
static void ATDetachCheckNoForeignKeyRefs(Relation partition)
void StartTransactionCommand(void)
Definition xact.c:3112
void CommitTransactionCommand(void)
Definition xact.c:3210

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

22207{
22208 Relation partRel;
22209 ObjectAddress address;
22211
22213
22214 /*
22215 * Wait until existing snapshots are gone. This is important if the
22216 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
22217 * user could immediately run DETACH FINALIZE without actually waiting for
22218 * existing transactions. We must not complete the detach action until
22219 * all such queries are complete (otherwise we would present them with an
22220 * inconsistent view of catalogs).
22221 */
22222 WaitForOlderSnapshots(snap->xmin, false);
22223
22224 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
22225
22227
22228 table_close(partRel, NoLock);
22229
22230 return address;
22231}
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition indexcmds.c:446
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 17185 of file tablecmds.c.

17186{
17187 mark_index_clustered(rel, InvalidOid, false);
17188}

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

9424{
9425 HeapTuple tuple;
9428 List *children;
9429 ObjectAddress object;
9430 bool is_expr;
9431
9432 /* At top level, permission check was done in ATPrepCmd, else do it */
9433 if (recursing)
9436
9437 /* Initialize addrs on the first invocation */
9438 Assert(!recursing || addrs != NULL);
9439
9440 /* since this function recurses, it could be driven to stack overflow */
9442
9443 if (!recursing)
9444 addrs = new_object_addresses();
9445
9446 /*
9447 * get the number of the attribute
9448 */
9450 if (!HeapTupleIsValid(tuple))
9451 {
9452 if (!missing_ok)
9453 {
9454 ereport(ERROR,
9456 errmsg("column \"%s\" of relation \"%s\" does not exist",
9458 }
9459 else
9460 {
9462 (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
9464 return InvalidObjectAddress;
9465 }
9466 }
9468
9469 attnum = targetatt->attnum;
9470
9471 /* Can't drop a system attribute */
9472 if (attnum <= 0)
9473 ereport(ERROR,
9475 errmsg("cannot drop system column \"%s\"",
9476 colName)));
9477
9478 /*
9479 * Don't drop inherited columns, unless recursing (presumably from a drop
9480 * of the parent column)
9481 */
9482 if (targetatt->attinhcount > 0 && !recursing)
9483 ereport(ERROR,
9485 errmsg("cannot drop inherited column \"%s\"",
9486 colName)));
9487
9488 /*
9489 * Don't drop columns used in the partition key, either. (If we let this
9490 * go through, the key column's dependencies would cause a cascaded drop
9491 * of the whole table, which is surely not what the user expected.)
9492 */
9493 if (has_partition_attrs(rel,
9495 &is_expr))
9496 ereport(ERROR,
9498 errmsg("cannot drop column \"%s\" because it is part of the partition key of relation \"%s\"",
9500
9501 ReleaseSysCache(tuple);
9502
9503 /*
9504 * Propagate to children as appropriate. Unlike most other ALTER
9505 * routines, we have to do this one level of recursion at a time; we can't
9506 * use find_all_inheritors to do it in one pass.
9507 */
9508 children =
9510
9511 if (children)
9512 {
9514 ListCell *child;
9515
9516 /*
9517 * In case of a partitioned table, the column must be dropped from the
9518 * partitions as well.
9519 */
9520 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && !recurse)
9521 ereport(ERROR,
9523 errmsg("cannot drop column from only the partitioned table when partitions exist"),
9524 errhint("Do not specify the ONLY keyword.")));
9525
9527 foreach(child, children)
9528 {
9529 Oid childrelid = lfirst_oid(child);
9532
9533 /* find_inheritance_children already got lock */
9536
9538 if (!HeapTupleIsValid(tuple)) /* shouldn't happen */
9539 elog(ERROR, "cache lookup failed for attribute \"%s\" of relation %u",
9542
9543 if (childatt->attinhcount <= 0) /* shouldn't happen */
9544 elog(ERROR, "relation %u has non-inherited attribute \"%s\"",
9546
9547 if (recurse)
9548 {
9549 /*
9550 * If the child column has other definition sources, just
9551 * decrement its inheritance count; if not, recurse to delete
9552 * it.
9553 */
9554 if (childatt->attinhcount == 1 && !childatt->attislocal)
9555 {
9556 /* Time to delete this child column, too */
9558 behavior, true, true,
9559 false, lockmode, addrs);
9560 }
9561 else
9562 {
9563 /* Child column must survive my deletion */
9564 childatt->attinhcount--;
9565
9566 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9567
9568 /* Make update visible */
9570 }
9571 }
9572 else
9573 {
9574 /*
9575 * If we were told to drop ONLY in this table (no recursion),
9576 * we need to mark the inheritors' attributes as locally
9577 * defined rather than inherited.
9578 */
9579 childatt->attinhcount--;
9580 childatt->attislocal = true;
9581
9582 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
9583
9584 /* Make update visible */
9586 }
9587
9588 heap_freetuple(tuple);
9589
9591 }
9593 }
9594
9595 /* Add object to delete */
9596 object.classId = RelationRelationId;
9597 object.objectId = RelationGetRelid(rel);
9598 object.objectSubId = attnum;
9599 add_exact_object_address(&object, addrs);
9600
9601 if (!recursing)
9602 {
9603 /* Recursion has ended, drop everything that was collected */
9604 performMultipleDeletions(addrs, behavior, 0);
9605 free_object_addresses(addrs);
9606 }
9607
9608 return object;
9609}
Bitmapset * bms_make_singleton(int x)
Definition bitmapset.c:217
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 14549 of file tablecmds.c.

14552{
14554 SysScanDesc scan;
14555 ScanKeyData skey[3];
14556 HeapTuple tuple;
14557 bool found = false;
14558
14560
14561 /*
14562 * Find and drop the target constraint
14563 */
14564 ScanKeyInit(&skey[0],
14568 ScanKeyInit(&skey[1],
14572 ScanKeyInit(&skey[2],
14577 true, NULL, 3, skey);
14578
14579 /* There can be at most one matching row */
14580 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14581 {
14582 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14583 missing_ok, lockmode);
14584 found = true;
14585 }
14586
14587 systable_endscan(scan);
14588
14589 if (!found)
14590 {
14591 if (!missing_ok)
14592 ereport(ERROR,
14594 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14596 else
14598 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14600 }
14601
14603}
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 8937 of file tablecmds.c.

8938{
8939 HeapTuple tuple;
8944 ObjectAddress address;
8945
8948 if (!HeapTupleIsValid(tuple))
8949 ereport(ERROR,
8951 errmsg("column \"%s\" of relation \"%s\" does not exist",
8953
8955 attnum = attTup->attnum;
8956
8957 if (attnum <= 0)
8958 ereport(ERROR,
8960 errmsg("cannot alter system column \"%s\"",
8961 colName)));
8962
8963 /*
8964 * TODO: This could be done, but it would need a table rewrite to
8965 * materialize the generated values. Note that for the time being, we
8966 * still error with missing_ok, so that we don't silently leave the column
8967 * as generated.
8968 */
8969 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
8970 ereport(ERROR,
8972 errmsg("ALTER TABLE / DROP EXPRESSION is not supported for virtual generated columns"),
8973 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8975
8976 if (!attTup->attgenerated)
8977 {
8978 if (!missing_ok)
8979 ereport(ERROR,
8981 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8983 else
8984 {
8986 (errmsg("column \"%s\" of relation \"%s\" is not a generated column, skipping",
8988 heap_freetuple(tuple);
8990 return InvalidObjectAddress;
8991 }
8992 }
8993
8994 /*
8995 * Mark the column as no longer generated. (The atthasdef flag needs to
8996 * get cleared too, but RemoveAttrDefault will handle that.)
8997 */
8998 attTup->attgenerated = '\0';
8999 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9000
9002 RelationGetRelid(rel),
9003 attnum);
9004 heap_freetuple(tuple);
9005
9007
9008 /*
9009 * Drop the dependency records of the GENERATED expression, in particular
9010 * its INTERNAL dependency on the column, which would otherwise cause
9011 * dependency.c to refuse to perform the deletion.
9012 */
9014 if (!OidIsValid(attrdefoid))
9015 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
9016 RelationGetRelid(rel), attnum);
9018
9019 /* Make above changes visible */
9021
9022 /*
9023 * Get rid of the GENERATED expression itself. We use RESTRICT here for
9024 * safety, but at present we do not expect anything to depend on the
9025 * default.
9026 */
9028 false, false);
9029
9031 RelationGetRelid(rel), attnum);
9032 return address;
9033}

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

8631{
8632 HeapTuple tuple;
8636 ObjectAddress address;
8637 Oid seqid;
8639 bool ispartitioned;
8640
8641 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8642 if (ispartitioned && !recurse)
8643 ereport(ERROR,
8645 errmsg("cannot drop identity from a column of only the partitioned table"),
8646 errhint("Do not specify the ONLY keyword.")));
8647
8648 if (rel->rd_rel->relispartition && !recursing)
8649 ereport(ERROR,
8651 errmsg("cannot drop identity from a column of a partition"));
8652
8655 if (!HeapTupleIsValid(tuple))
8656 ereport(ERROR,
8658 errmsg("column \"%s\" of relation \"%s\" does not exist",
8660
8662 attnum = attTup->attnum;
8663
8664 if (attnum <= 0)
8665 ereport(ERROR,
8667 errmsg("cannot alter system column \"%s\"",
8668 colName)));
8669
8670 if (!attTup->attidentity)
8671 {
8672 if (!missing_ok)
8673 ereport(ERROR,
8675 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8677 else
8678 {
8680 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8682 heap_freetuple(tuple);
8684 return InvalidObjectAddress;
8685 }
8686 }
8687
8688 attTup->attidentity = '\0';
8689 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8690
8692 RelationGetRelid(rel),
8693 attTup->attnum);
8695 RelationGetRelid(rel), attnum);
8696 heap_freetuple(tuple);
8697
8699
8700 /*
8701 * Recurse to drop the identity from column in partitions. Identity is
8702 * not inherited in regular inheritance children so ignore them.
8703 */
8704 if (recurse && ispartitioned)
8705 {
8706 List *children;
8707 ListCell *lc;
8708
8709 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8710
8711 foreach(lc, children)
8712 {
8714
8716 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8718 }
8719 }
8720
8721 if (!recursing)
8722 {
8723 /* drop the internal sequence */
8724 seqid = getIdentitySequence(rel, attnum, false);
8729 seqaddress.objectId = seqid;
8730 seqaddress.objectSubId = 0;
8732 }
8733
8734 return address;
8735}
void performDeletion(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition dependency.c:279
#define PERFORM_DELETION_INTERNAL
Definition dependency.h:92
long deleteDependencyRecordsForClass(Oid classId, Oid objectId, Oid refclassId, char deptype)
Definition pg_depend.c:364
Oid getIdentitySequence(Relation rel, AttrNumber attnum, bool missing_ok)
Definition pg_depend.c:1149

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

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

◆ ATExecDropInherit()

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

Definition at line 18533 of file tablecmds.c.

18534{
18535 ObjectAddress address;
18537
18538 /*
18539 * AccessShareLock on the parent is probably enough, seeing that DROP
18540 * TABLE doesn't lock parent tables at all. We need some lock since we'll
18541 * be inspecting the parent's schema.
18542 */
18544
18545 /*
18546 * We don't bother to check ownership of the parent table --- ownership of
18547 * the child is presumed enough rights.
18548 */
18549
18550 /* Off to RemoveInheritance() where most of the work happens */
18551 RemoveInheritance(rel, parent_rel, false);
18552
18555
18556 /* keep our lock on the parent relation until commit */
18558
18559 return address;
18560}

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

7885{
7886 HeapTuple tuple;
7891 ObjectAddress address;
7892
7893 /*
7894 * lookup the attribute
7895 */
7897
7899 if (!HeapTupleIsValid(tuple))
7900 ereport(ERROR,
7902 errmsg("column \"%s\" of relation \"%s\" does not exist",
7905 attnum = attTup->attnum;
7907 RelationGetRelid(rel), attnum);
7908
7909 /* If the column is already nullable there's nothing to do. */
7910 if (!attTup->attnotnull)
7911 {
7913 return InvalidObjectAddress;
7914 }
7915
7916 /* Prevent them from altering a system attribute */
7917 if (attnum <= 0)
7918 ereport(ERROR,
7920 errmsg("cannot alter system column \"%s\"",
7921 colName)));
7922
7923 if (attTup->attidentity)
7924 ereport(ERROR,
7926 errmsg("column \"%s\" of relation \"%s\" is an identity column",
7928
7929 /*
7930 * If rel is partition, shouldn't drop NOT NULL if parent has the same.
7931 */
7932 if (rel->rd_rel->relispartition)
7933 {
7936 TupleDesc tupDesc = RelationGetDescr(parent);
7938
7940 if (TupleDescAttr(tupDesc, parent_attnum - 1)->attnotnull)
7941 ereport(ERROR,
7943 errmsg("column \"%s\" is marked NOT NULL in parent table",
7944 colName)));
7946 }
7947
7948 /*
7949 * Find the constraint that makes this column NOT NULL, and drop it.
7950 * dropconstraint_internal() resets attnotnull.
7951 */
7953 if (conTup == NULL)
7954 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
7956
7957 /* The normal case: we have a pg_constraint row, remove it */
7958 dropconstraint_internal(rel, conTup, DROP_RESTRICT, recurse, false,
7959 false, lockmode);
7961
7963 RelationGetRelid(rel), attnum);
7964
7966
7967 return address;
7968}
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 19061 of file tablecmds.c.

19062{
19063 Oid relid = RelationGetRelid(rel);
19065 HeapTuple tuple;
19066
19067 if (!OidIsValid(rel->rd_rel->reloftype))
19068 ereport(ERROR,
19070 errmsg("\"%s\" is not a typed table",
19072
19073 /*
19074 * We don't bother to check ownership of the type --- ownership of the
19075 * table is presumed enough rights. No lock required on the type, either.
19076 */
19077
19078 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
19080
19081 /* Clear pg_class.reloftype */
19084 if (!HeapTupleIsValid(tuple))
19085 elog(ERROR, "cache lookup failed for relation %u", relid);
19086 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
19088
19090
19091 heap_freetuple(tuple);
19093}

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

17936{
17937 EnableDisableRule(rel, rulename, fires_when);
17938
17940 RelationGetRelid(rel), 0);
17941}
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 17916 of file tablecmds.c.

17919{
17920 EnableDisableTrigger(rel, trigname, InvalidOid,
17921 fires_when, skip_system, recurse,
17922 lockmode);
17923
17925 RelationGetRelid(rel), 0);
17926}
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition trigger.c:1753

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

Referenced by ATExecCmd().

◆ ATExecForceNoForceRowSecurity()

static void ATExecForceNoForceRowSecurity ( Relation  rel,
bool  force_rls 
)
static

Definition at line 19337 of file tablecmds.c.

19338{
19340 Oid relid;
19341 HeapTuple tuple;
19342
19343 relid = RelationGetRelid(rel);
19344
19346
19348
19349 if (!HeapTupleIsValid(tuple))
19350 elog(ERROR, "cache lookup failed for relation %u", relid);
19351
19353 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
19354
19356 RelationGetRelid(rel), 0);
19357
19359 heap_freetuple(tuple);
19360}

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

19367{
19369 ForeignServer *server;
19371 HeapTuple tuple;
19372 bool isnull;
19376 Datum datum;
19378
19379 if (options == NIL)
19380 return;
19381
19383
19385 ObjectIdGetDatum(rel->rd_id));
19386 if (!HeapTupleIsValid(tuple))
19387 ereport(ERROR,
19389 errmsg("foreign table \"%s\" does not exist",
19392 server = GetForeignServer(tableform->ftserver);
19393 fdw = GetForeignDataWrapper(server->fdwid);
19394
19395 memset(repl_val, 0, sizeof(repl_val));
19396 memset(repl_null, false, sizeof(repl_null));
19397 memset(repl_repl, false, sizeof(repl_repl));
19398
19399 /* Extract the current options */
19401 tuple,
19403 &isnull);
19404 if (isnull)
19405 datum = PointerGetDatum(NULL);
19406
19407 /* Transform the options */
19409 datum,
19410 options,
19411 fdw->fdwvalidator);
19412
19413 if (DatumGetPointer(datum) != NULL)
19415 else
19417
19419
19420 /* Everything looks good - update the tuple */
19421
19424
19425 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
19426
19427 /*
19428 * Invalidate relcache so that all sessions will refresh any cached plans
19429 * that might depend on the old options.
19430 */
19432
19434 RelationGetRelid(rel), 0);
19435
19437
19438 heap_freetuple(tuple);
19439}

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

23758{
23761 List *extDepState = NIL;
23764 Oid ownerId = InvalidOid;
23765 Oid save_userid;
23766 int save_sec_context;
23767 int save_nestlevel;
23768
23769 /*
23770 * Check ownership of merged partitions - partitions with different owners
23771 * cannot be merged. Also, collect the OIDs of these partitions during the
23772 * check.
23773 */
23775 {
23777
23778 /*
23779 * We are going to detach and remove this partition. We already took
23780 * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
23781 * NoLock is fine.
23782 */
23785
23786 if (OidIsValid(ownerId))
23787 {
23788 /* Do the partitions being merged have different owners? */
23789 if (ownerId != mergingPartition->rd_rel->relowner)
23790 ereport(ERROR,
23792 errmsg("partitions being merged have different owners"));
23793 }
23794 else
23795 ownerId = mergingPartition->rd_rel->relowner;
23796
23797 /* Store the next merging partition into the list. */
23800
23802 }
23803
23804 /* Look up the existing relation by the new partition name. */
23806
23807 /*
23808 * Check if this name is already taken. This helps us to detect the
23809 * situation when one of the merging partitions has the same name as the
23810 * new partition. Otherwise, this would fail later on anyway, but
23811 * catching this here allows us to emit a nicer error message.
23812 */
23814 {
23816 {
23817 /*
23818 * The new partition has the same name as one of the merging
23819 * partitions.
23820 */
23821 char tmpRelName[NAMEDATALEN];
23822
23823 /* Generate a temporary name. */
23824 sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23825
23826 /*
23827 * Rename the existing partition with a temporary name, leaving it
23828 * free for the new partition. We don't need to care about this
23829 * in the future because we're going to eventually drop the
23830 * existing partition anyway.
23831 */
23833
23834 /*
23835 * We must bump the command counter to make the new partition
23836 * tuple visible for rename.
23837 */
23839 }
23840 else
23841 {
23842 ereport(ERROR,
23844 errmsg("relation \"%s\" already exists", cmd->name->relname));
23845 }
23846 }
23847
23850
23851 /*
23852 * Collect extension dependencies from indexes on the merging partitions.
23853 * We must do this before detaching them, so we can restore the
23854 * dependencies on the new partition's indexes later.
23855 */
23857
23858 /* Detach all merging partitions. */
23860 {
23862
23864
23866
23868 }
23869
23870 /*
23871 * Perform a preliminary check to determine whether it's safe to drop all
23872 * merging partitions before we actually do so later. After merging rows
23873 * into the new partitions via MergePartitionsMoveRows, all old partitions
23874 * need to be dropped. However, since the drop behavior is DROP_RESTRICT
23875 * and the merge process (MergePartitionsMoveRows) can be time-consuming,
23876 * performing an early check on the drop eligibility of old partitions is
23877 * preferable.
23878 */
23880 {
23881 ObjectAddress object;
23882
23883 /* Get oid of the later to be dropped relation. */
23885 object.classId = RelationRelationId;
23886 object.objectSubId = 0;
23887
23889 }
23890
23891 /*
23892 * Create a table for the new partition, using the partitioned table as a
23893 * model.
23894 */
23895 Assert(OidIsValid(ownerId));
23896 newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
23897
23898 /*
23899 * Switch to the table owner's userid, so that any index functions are run
23900 * as that user. Also, lockdown security-restricted operations and
23901 * arrange to make GUC variable changes local to this command.
23902 *
23903 * Need to do it after determining the namespace in the
23904 * createPartitionTable() call.
23905 */
23906 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23907 SetUserIdAndSecContext(ownerId,
23908 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23909 save_nestlevel = NewGUCNestLevel();
23911
23912 /* Copy data from merged partitions to the new partition. */
23914
23915 /* Drop the current partitions before attaching the new one. */
23917 {
23918 ObjectAddress object;
23919
23921 object.classId = RelationRelationId;
23922 object.objectSubId = 0;
23923
23924 performDeletion(&object, DROP_RESTRICT, 0);
23925 }
23926
23928
23929 /*
23930 * Attach a new partition to the partitioned table. wqueue = NULL:
23931 * verification for each cloned constraint is not needed.
23932 */
23934
23935 /*
23936 * Apply extension dependencies to the new partition's indexes. This
23937 * preserves any "DEPENDS ON EXTENSION" settings from the merged
23938 * partitions.
23939 */
23941
23943
23944 /* Keep the lock until commit. */
23946
23947 /* Roll back any GUC changes executed by index functions. */
23948 AtEOXact_GUC(false, save_nestlevel);
23949
23950 /* Restore the userid and security context. */
23951 SetUserIdAndSecContext(save_userid, save_sec_context);
23952}
void performDeletionCheck(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition dependency.c:338
int MyProcPid
Definition globals.c:49
int NewGUCNestLevel(void)
Definition guc.c:2142
void RestrictSearchPath(void)
Definition guc.c:2153
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition guc.c:2169
#define SECURITY_RESTRICTED_OPERATION
Definition miscadmin.h:322
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition miscinit.c:613
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition miscinit.c:620
#define sprintf
Definition port.h:263
List * partlist
Relation table_openrv_extended(const RangeVar *relation, LOCKMODE lockmode, bool missing_ok)
Definition table.c:103
static void MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
static void applyPartitionIndexExtDeps(Oid newPartOid, List *extDepState)
static Relation createPartitionTable(List **wqueue, RangeVar *newPartName, Relation parent_rel, Oid ownerId)
static void detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
static List * collectPartitionIndexExtDeps(List *partitionOids)
static void freePartitionIndexExtDeps(List *extDepState)

References AccessExclusiveLock, applyPartitionIndexExtDeps(), Assert, AtEOXact_GUC(), attachPartitionTable(), PartitionCmd::bound, CheckRelationLockedByMe(), collectPartitionIndexExtDeps(), CommandCounterIncrement(), createPartitionTable(), detachPartitionTable(), DROP_RESTRICT, ereport, errcode(), errmsg, ERROR, fb(), foreach_node, foreach_oid, freePartitionIndexExtDeps(), get_default_oid_from_partdesc(), GetUserIdAndSecContext(), InvalidOid, lappend_oid(), list_free(), list_member_oid(), MergePartitionsMoveRows(), MyProcPid, name, PartitionCmd::name, NAMEDATALEN, NewGUCNestLevel(), NIL, NoLock, ObjectAddress::objectId, OidIsValid, PartitionCmd::partlist, 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 19193 of file tablecmds.c.

19194{
19195 Oid indexOid;
19196 Relation indexRel;
19197 int key;
19198
19199 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
19200 {
19201 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
19202 return;
19203 }
19204 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
19205 {
19206 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
19207 return;
19208 }
19209 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
19210 {
19211 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
19212 return;
19213 }
19214 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
19215 {
19216 /* fallthrough */ ;
19217 }
19218 else
19219 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
19220
19221 /* Check that the index exists */
19222 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
19223 if (!OidIsValid(indexOid))
19224 ereport(ERROR,
19226 errmsg("index \"%s\" for table \"%s\" does not exist",
19227 stmt->name, RelationGetRelationName(rel))));
19228
19229 indexRel = index_open(indexOid, ShareLock);
19230
19231 /* Check that the index is on the relation we're altering. */
19232 if (indexRel->rd_index == NULL ||
19233 indexRel->rd_index->indrelid != RelationGetRelid(rel))
19234 ereport(ERROR,
19236 errmsg("\"%s\" is not an index for table \"%s\"",
19237 RelationGetRelationName(indexRel),
19239
19240 /*
19241 * The AM must support uniqueness, and the index must in fact be unique.
19242 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
19243 * exclusion), we can use that too.
19244 */
19245 if ((!indexRel->rd_indam->amcanunique ||
19246 !indexRel->rd_index->indisunique) &&
19247 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
19248 ereport(ERROR,
19250 errmsg("cannot use non-unique index \"%s\" as replica identity",
19251 RelationGetRelationName(indexRel))));
19252 /* Deferred indexes are not guaranteed to be always unique. */
19253 if (!indexRel->rd_index->indimmediate)
19254 ereport(ERROR,
19256 errmsg("cannot use non-immediate index \"%s\" as replica identity",
19257 RelationGetRelationName(indexRel))));
19258 /* Expression indexes aren't supported. */
19259 if (RelationGetIndexExpressions(indexRel) != NIL)
19260 ereport(ERROR,
19262 errmsg("cannot use expression index \"%s\" as replica identity",
19263 RelationGetRelationName(indexRel))));
19264 /* Predicate indexes aren't supported. */
19265 if (RelationGetIndexPredicate(indexRel) != NIL)
19266 ereport(ERROR,
19268 errmsg("cannot use partial index \"%s\" as replica identity",
19269 RelationGetRelationName(indexRel))));
19270
19271 /* Check index for nullable columns. */
19272 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
19273 {
19274 int16 attno = indexRel->rd_index->indkey.values[key];
19275 Form_pg_attribute attr;
19276
19277 /*
19278 * Reject any other system columns. (Going forward, we'll disallow
19279 * indexes containing such columns in the first place, but they might
19280 * exist in older branches.)
19281 */
19282 if (attno <= 0)
19283 ereport(ERROR,
19285 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
19286 RelationGetRelationName(indexRel), attno)));
19287
19288 attr = TupleDescAttr(rel->rd_att, attno - 1);
19289 if (!attr->attnotnull)
19290 ereport(ERROR,
19292 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
19293 RelationGetRelationName(indexRel),
19294 NameStr(attr->attname))));
19295 }
19296
19297 /* This index is suitable for use as a replica identity. Mark it. */
19298 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
19299
19300 index_close(indexRel, NoLock);
19301}
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition rel.h:535
List * RelationGetIndexPredicate(Relation relation)
Definition relcache.c:5222
List * RelationGetIndexExpressions(Relation relation)
Definition relcache.c:5109
bool amcanunique
Definition amapi.h:259
const struct IndexAmRoutine * rd_indam
Definition rel.h:206
TupleDesc rd_att
Definition rel.h:112
static void relation_mark_replica_identity(Relation rel, char ri_type, Oid indexOid, bool is_internal)

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

Referenced by ATExecCmd().

◆ ATExecSetAccessMethodNoStorage()

static void ATExecSetAccessMethodNoStorage ( Relation  rel,
Oid  newAccessMethodId 
)
static

Definition at line 17231 of file tablecmds.c.

17232{
17235 HeapTuple tuple;
17236 Form_pg_class rd_rel;
17237 Oid reloid = RelationGetRelid(rel);
17238
17239 /*
17240 * Shouldn't be called on relations having storage; these are processed in
17241 * phase 3.
17242 */
17243 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
17244
17245 /* Get a modifiable copy of the relation's pg_class row. */
17247
17249 if (!HeapTupleIsValid(tuple))
17250 elog(ERROR, "cache lookup failed for relation %u", reloid);
17251 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
17252
17253 /* Update the pg_class row. */
17254 oldAccessMethodId = rd_rel->relam;
17255 rd_rel->relam = newAccessMethodId;
17256
17257 /* Leave if no update required */
17258 if (rd_rel->relam == oldAccessMethodId)
17259 {
17260 heap_freetuple(tuple);
17262 return;
17263 }
17264
17265 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17266
17267 /*
17268 * Update the dependency on the new access method. No dependency is added
17269 * if the new access method is InvalidOid (default case). Be very careful
17270 * that this has to compare the previous value stored in pg_class with the
17271 * new one.
17272 */
17273 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
17274 {
17276 referenced;
17277
17278 /*
17279 * New access method is defined and there was no dependency
17280 * previously, so record a new one.
17281 */
17285 }
17286 else if (OidIsValid(oldAccessMethodId) &&
17287 !OidIsValid(rd_rel->relam))
17288 {
17289 /*
17290 * There was an access method defined, and no new one, so just remove
17291 * the existing dependency.
17292 */
17296 }
17297 else
17298 {
17300 OidIsValid(rd_rel->relam));
17301
17302 /* Both are valid, so update the dependency */
17305 oldAccessMethodId, rd_rel->relam);
17306 }
17307
17308 /* make the relam and dependency changes visible */
17310
17312
17313 heap_freetuple(tuple);
17315}

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

19451{
19453 HeapTuple tuple;
19456 char *compression;
19457 char cmethod;
19458 ObjectAddress address;
19459
19460 compression = strVal(newValue);
19461
19463
19464 /* copy the cache entry so we can scribble on it below */
19466 if (!HeapTupleIsValid(tuple))
19467 ereport(ERROR,
19469 errmsg("column \"%s\" of relation \"%s\" does not exist",
19471
19472 /* prevent them from altering a system attribute */
19474 attnum = atttableform->attnum;
19475 if (attnum <= 0)
19476 ereport(ERROR,
19478 errmsg("cannot alter system column \"%s\"", column)));
19479
19480 /*
19481 * Check that column type is compressible, then get the attribute
19482 * compression method code
19483 */
19484 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
19485
19486 /* update pg_attribute entry */
19487 atttableform->attcompression = cmethod;
19488 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
19489
19491 RelationGetRelid(rel),
19492 attnum);
19493
19494 /*
19495 * Apply the change to indexes as well (only for simple index columns,
19496 * matching behavior of index.c ConstructTupleDescriptor()).
19497 */
19499 false, 0,
19500 true, cmethod,
19501 lockmode);
19502
19503 heap_freetuple(tuple);
19504
19506
19507 /* make changes visible */
19509
19511 RelationGetRelid(rel), attnum);
19512 return address;
19513}
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:9265

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

8745{
8746 HeapTuple tuple;
8749 char attgenerated;
8750 bool rewrite;
8752 ObjectAddress address;
8753 Expr *defval;
8756
8758 if (!HeapTupleIsValid(tuple))
8759 ereport(ERROR,
8761 errmsg("column \"%s\" of relation \"%s\" does not exist",
8763
8765
8766 attnum = attTup->attnum;
8767 if (attnum <= 0)
8768 ereport(ERROR,
8770 errmsg("cannot alter system column \"%s\"",
8771 colName)));
8772
8773 attgenerated = attTup->attgenerated;
8774 if (!attgenerated)
8775 ereport(ERROR,
8777 errmsg("column \"%s\" of relation \"%s\" is not a generated column",
8779
8780 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL && attTup->attnotnull)
8781 tab->verify_new_notnull = true;
8782
8783 /*
8784 * We need to prevent this because a change of expression could affect a
8785 * row filter and inject expressions that are not permitted in a row
8786 * filter. XXX We could try to have a more precise check to catch only
8787 * publications with row filters, or even re-verify the row filter
8788 * expressions.
8789 */
8790 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL &&
8792 ereport(ERROR,
8794 errmsg("ALTER TABLE / SET EXPRESSION is not supported for virtual generated columns in tables that are part of a publication"),
8795 errdetail("Column \"%s\" of relation \"%s\" is a virtual generated column.",
8797
8798 rewrite = (attgenerated == ATTRIBUTE_GENERATED_STORED);
8799
8800 ReleaseSysCache(tuple);
8801
8802 if (rewrite)
8803 {
8804 /*
8805 * Clear all the missing values if we're rewriting the table, since
8806 * this renders them pointless.
8807 */
8809
8810 /* make sure we don't conflict with later attribute modifications */
8812 }
8813
8814 /*
8815 * Find everything that depends on the column (constraints, indexes, etc),
8816 * and record enough information to let us recreate the objects.
8817 */
8819
8820 /*
8821 * Find whole-row referenced objects that depend on the column
8822 * (constraints, indexes, etc.), and record enough information to let us
8823 * recreate the objects.
8824 */
8826
8827 /*
8828 * Drop the dependency records of the GENERATED expression, in particular
8829 * its INTERNAL dependency on the column, which would otherwise cause
8830 * dependency.c to refuse to perform the deletion.
8831 */
8833 if (!OidIsValid(attrdefoid))
8834 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
8835 RelationGetRelid(rel), attnum);
8837
8838 /* Make above changes visible */
8840
8841 /*
8842 * Get rid of the GENERATED expression itself. We use RESTRICT here for
8843 * safety, but at present we do not expect anything to depend on the
8844 * expression.
8845 */
8847 false, false);
8848
8849 /* Prepare to store the new expression, in the catalogs */
8851 rawEnt->attnum = attnum;
8852 rawEnt->raw_default = newExpr;
8853 rawEnt->generated = attgenerated;
8854
8855 /* Store the generated expression */
8857 false, true, false, NULL);
8858
8859 /* Make above new expression visible */
8861
8862 if (rewrite)
8863 {
8864 /* Prepare for table rewrite */
8865 defval = (Expr *) build_column_default(rel, attnum);
8866
8868 newval->attnum = attnum;
8869 newval->expr = expression_planner(defval);
8870 newval->is_generated = true;
8871
8872 tab->newvals = lappend(tab->newvals, newval);
8874 }
8875
8876 /* Drop any pg_statistic entry for the column */
8878
8880 RelationGetRelid(rel), attnum);
8881
8883 RelationGetRelid(rel), attnum);
8884 return address;
8885}
List * GetRelationIncludedPublications(Oid relid)
static void RememberWholeRowDependentForRebuilding(AlteredTableInfo *tab, AlterTableType subtype, Relation rel)

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

8514{
8517 HeapTuple tuple;
8521 ObjectAddress address;
8522 bool ispartitioned;
8523
8524 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8525 if (ispartitioned && !recurse)
8526 ereport(ERROR,
8528 errmsg("cannot change identity column of only the partitioned table"),
8529 errhint("Do not specify the ONLY keyword.")));
8530
8531 if (rel->rd_rel->relispartition && !recursing)
8532 ereport(ERROR,
8534 errmsg("cannot change identity column of a partition"));
8535
8536 foreach(option, castNode(List, def))
8537 {
8539
8540 if (strcmp(defel->defname, "generated") == 0)
8541 {
8542 if (generatedEl)
8543 ereport(ERROR,
8545 errmsg("conflicting or redundant options")));
8547 }
8548 else
8549 elog(ERROR, "option \"%s\" not recognized",
8550 defel->defname);
8551 }
8552
8553 /*
8554 * Even if there is nothing to change here, we run all the checks. There
8555 * will be a subsequent ALTER SEQUENCE that relies on everything being
8556 * there.
8557 */
8558
8561 if (!HeapTupleIsValid(tuple))
8562 ereport(ERROR,
8564 errmsg("column \"%s\" of relation \"%s\" does not exist",
8566
8568 attnum = attTup->attnum;
8569
8570 if (attnum <= 0)
8571 ereport(ERROR,
8573 errmsg("cannot alter system column \"%s\"",
8574 colName)));
8575
8576 if (!attTup->attidentity)
8577 ereport(ERROR,
8579 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8581
8582 if (generatedEl)
8583 {
8584 attTup->attidentity = defGetInt32(generatedEl);
8585 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8586
8588 RelationGetRelid(rel),
8589 attTup->attnum);
8591 RelationGetRelid(rel), attnum);
8592 }
8593 else
8594 address = InvalidObjectAddress;
8595
8596 heap_freetuple(tuple);
8598
8599 /*
8600 * Recurse to propagate the identity change to partitions. Identity is not
8601 * inherited in regular inheritance children.
8602 */
8603 if (generatedEl && recurse && ispartitioned)
8604 {
8605 List *children;
8606 ListCell *lc;
8607
8608 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8609
8610 foreach(lc, children)
8611 {
8613
8615 ATExecSetIdentity(childrel, colName, def, lockmode, recurse, true);
8617 }
8618 }
8619
8620 return address;
8621}
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 8054 of file tablecmds.c.

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

9188{
9190 HeapTuple tuple,
9191 newtuple;
9194 Datum datum,
9195 newOptions;
9196 bool isnull;
9197 ObjectAddress address;
9201
9203
9205
9206 if (!HeapTupleIsValid(tuple))
9207 ereport(ERROR,
9209 errmsg("column \"%s\" of relation \"%s\" does not exist",
9212
9213 attnum = attrtuple->attnum;
9214 if (attnum <= 0)
9215 ereport(ERROR,
9217 errmsg("cannot alter system column \"%s\"",
9218 colName)));
9219
9220 /* Generate new proposed attoptions (text array) */
9222 &isnull);
9223 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9225 false, isReset);
9226 /* Validate new options */
9228
9229 /* Build new tuple. */
9230 memset(repl_null, false, sizeof(repl_null));
9231 memset(repl_repl, false, sizeof(repl_repl));
9232 if (newOptions != (Datum) 0)
9234 else
9239
9240 /* Update system catalog. */
9241 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9242
9244 RelationGetRelid(rel),
9245 attrtuple->attnum);
9247 RelationGetRelid(rel), attnum);
9248
9249 heap_freetuple(newtuple);
9250
9251 ReleaseSysCache(tuple);
9252
9254
9255 return address;
9256}
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 17351 of file tablecmds.c.

17353{
17354 Oid relid;
17356 HeapTuple tuple;
17357 HeapTuple newtuple;
17358 Datum datum;
17363 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
17364
17366 return; /* nothing to do */
17367
17369
17370 /* Fetch heap tuple */
17371 relid = RelationGetRelid(rel);
17373 if (!HeapTupleIsValid(tuple))
17374 elog(ERROR, "cache lookup failed for relation %u", relid);
17375
17377 {
17378 /*
17379 * If we're supposed to replace the reloptions list, we just pretend
17380 * there were none before.
17381 */
17382 datum = (Datum) 0;
17383 }
17384 else
17385 {
17386 bool isnull;
17387
17388 /* Get the old reloptions */
17390 &isnull);
17391 if (isnull)
17392 datum = (Datum) 0;
17393 }
17394
17395 /* Generate new proposed reloptions (text array) */
17398
17399 /* Validate */
17400 switch (rel->rd_rel->relkind)
17401 {
17402 case RELKIND_RELATION:
17403 case RELKIND_MATVIEW:
17404 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
17405 break;
17408 break;
17409 case RELKIND_VIEW:
17411 break;
17412 case RELKIND_INDEX:
17415 break;
17416 case RELKIND_TOASTVALUE:
17417 /* fall through to error -- shouldn't ever get here */
17418 default:
17419 ereport(ERROR,
17421 errmsg("cannot set options for relation \"%s\"",
17423 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
17424 break;
17425 }
17426
17427 /* Special-case validation of view options */
17428 if (rel->rd_rel->relkind == RELKIND_VIEW)
17429 {
17432 ListCell *cell;
17433 bool check_option = false;
17434
17435 foreach(cell, view_options)
17436 {
17437 DefElem *defel = (DefElem *) lfirst(cell);
17438
17439 if (strcmp(defel->defname, "check_option") == 0)
17440 check_option = true;
17441 }
17442
17443 /*
17444 * If the check option is specified, look to see if the view is
17445 * actually auto-updatable or not.
17446 */
17447 if (check_option)
17448 {
17449 const char *view_updatable_error =
17451
17453 ereport(ERROR,
17455 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
17456 errhint("%s", _(view_updatable_error))));
17457 }
17458 }
17459
17460 /*
17461 * All we need do here is update the pg_class row; the new options will be
17462 * propagated into relcaches during post-commit cache inval.
17463 */
17464 memset(repl_val, 0, sizeof(repl_val));
17465 memset(repl_null, false, sizeof(repl_null));
17466 memset(repl_repl, false, sizeof(repl_repl));
17467
17468 if (newOptions != (Datum) 0)
17470 else
17472
17474
17475 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17477
17478 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17480
17482
17483 heap_freetuple(newtuple);
17484
17485 ReleaseSysCache(tuple);
17486
17487 /* repeat the whole exercise for the toast table, if there's one */
17488 if (OidIsValid(rel->rd_rel->reltoastrelid))
17489 {
17491 Oid toastid = rel->rd_rel->reltoastrelid;
17492
17493 toastrel = table_open(toastid, lockmode);
17494
17495 /* Fetch heap tuple */
17497 if (!HeapTupleIsValid(tuple))
17498 elog(ERROR, "cache lookup failed for relation %u", toastid);
17499
17501 {
17502 /*
17503 * If we're supposed to replace the reloptions list, we just
17504 * pretend there were none before.
17505 */
17506 datum = (Datum) 0;
17507 }
17508 else
17509 {
17510 bool isnull;
17511
17512 /* Get the old reloptions */
17514 &isnull);
17515 if (isnull)
17516 datum = (Datum) 0;
17517 }
17518
17520 false, operation == AT_ResetRelOptions);
17521
17523
17524 memset(repl_val, 0, sizeof(repl_val));
17525 memset(repl_null, false, sizeof(repl_null));
17526 memset(repl_repl, false, sizeof(repl_repl));
17527
17528 if (newOptions != (Datum) 0)
17530 else
17532
17534
17535 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17537
17538 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17539
17542 InvalidOid, true);
17543
17544 heap_freetuple(newtuple);
17545
17546 ReleaseSysCache(tuple);
17547
17549 }
17550
17552}
static DataChecksumsWorkerOperation operation
#define InvokeObjectPostAlterHookArg(classId, objectId, subId, auxiliaryId, is_internal)
List * untransformRelOptions(Datum options)
bytea * view_reloptions(Datum reloptions, bool validate)
bytea * index_reloptions(amoptions_function amoptions, Datum reloptions, bool validate)
bytea * partitioned_table_reloptions(Datum reloptions, bool validate)
bytea * heap_reloptions(char relkind, Datum reloptions, bool validate)
#define HEAP_RELOPT_NAMESPACES
Definition reloptions.h:61
Query * get_view_query(Relation view)
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
amoptions_function amoptions
Definition amapi.h:305
HeapTuple SearchSysCacheLocked1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:283

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

Referenced by ATExecCmd().

◆ ATExecSetRowSecurity()

static void ATExecSetRowSecurity ( Relation  rel,
bool  rls 
)
static

Definition at line 19307 of file tablecmds.c.

19308{
19310 Oid relid;
19311 HeapTuple tuple;
19312
19313 relid = RelationGetRelid(rel);
19314
19315 /* Pull the record for this relation and update it */
19317
19319
19320 if (!HeapTupleIsValid(tuple))
19321 elog(ERROR, "cache lookup failed for relation %u", relid);
19322
19324 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
19325
19327 RelationGetRelid(rel), 0);
19328
19330 heap_freetuple(tuple);
19331}

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

9042{
9043 int newtarget = 0;
9044 bool newtarget_default;
9046 HeapTuple tuple,
9047 newtuple;
9050 ObjectAddress address;
9054
9055 /*
9056 * We allow referencing columns by numbers only for indexes, since table
9057 * column numbers could contain gaps if columns are later dropped.
9058 */
9059 if (rel->rd_rel->relkind != RELKIND_INDEX &&
9060 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
9061 !colName)
9062 ereport(ERROR,
9064 errmsg("cannot refer to non-index column by number")));
9065
9066 /* -1 was used in previous versions for the default setting */
9067 if (newValue && intVal(newValue) != -1)
9068 {
9070 newtarget_default = false;
9071 }
9072 else
9073 newtarget_default = true;
9074
9075 if (!newtarget_default)
9076 {
9077 /*
9078 * Limit target to a sane range
9079 */
9080 if (newtarget < 0)
9081 {
9082 ereport(ERROR,
9084 errmsg("statistics target %d is too low",
9085 newtarget)));
9086 }
9088 {
9092 errmsg("lowering statistics target to %d",
9093 newtarget)));
9094 }
9095 }
9096
9098
9099 if (colName)
9100 {
9102
9103 if (!HeapTupleIsValid(tuple))
9104 ereport(ERROR,
9106 errmsg("column \"%s\" of relation \"%s\" does not exist",
9108 }
9109 else
9110 {
9112
9113 if (!HeapTupleIsValid(tuple))
9114 ereport(ERROR,
9116 errmsg("column number %d of relation \"%s\" does not exist",
9118 }
9119
9121
9122 attnum = attrtuple->attnum;
9123 if (attnum <= 0)
9124 ereport(ERROR,
9126 errmsg("cannot alter system column \"%s\"",
9127 colName)));
9128
9129 /*
9130 * Prevent this as long as the ANALYZE code skips virtual generated
9131 * columns.
9132 */
9133 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
9134 ereport(ERROR,
9136 errmsg("cannot alter statistics on virtual generated column \"%s\"",
9137 colName)));
9138
9139 if (rel->rd_rel->relkind == RELKIND_INDEX ||
9140 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
9141 {
9142 if (attnum > rel->rd_index->indnkeyatts)
9143 ereport(ERROR,
9145 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9146 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9147 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9148 ereport(ERROR,
9150 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9151 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9152 errhint("Alter statistics on table column instead.")));
9153 }
9154
9155 /* Build new tuple. */
9156 memset(repl_null, false, sizeof(repl_null));
9157 memset(repl_repl, false, sizeof(repl_repl));
9158 if (!newtarget_default)
9160 else
9165 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9166
9168 RelationGetRelid(rel),
9169 attrtuple->attnum);
9171 RelationGetRelid(rel), attnum);
9172
9173 heap_freetuple(newtuple);
9174
9175 ReleaseSysCache(tuple);
9176
9178
9179 return address;
9180}
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 9328 of file tablecmds.c.

9329{
9331 HeapTuple tuple;
9334 ObjectAddress address;
9335
9337
9339
9340 if (!HeapTupleIsValid(tuple))
9341 ereport(ERROR,
9343 errmsg("column \"%s\" of relation \"%s\" does not exist",
9346
9347 attnum = attrtuple->attnum;
9348 if (attnum <= 0)
9349 ereport(ERROR,
9351 errmsg("cannot alter system column \"%s\"",
9352 colName)));
9353
9354 attrtuple->attstorage = GetAttributeStorage(attrtuple->atttypid, strVal(newValue));
9355
9356 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9357
9359 RelationGetRelid(rel),
9360 attrtuple->attnum);
9361
9362 /*
9363 * Apply the change to indexes as well (only for simple index columns,
9364 * matching behavior of index.c ConstructTupleDescriptor()).
9365 */
9367 true, attrtuple->attstorage,
9368 false, 0,
9369 lockmode);
9370
9371 heap_freetuple(tuple);
9372
9374
9376 RelationGetRelid(rel), attnum);
9377 return address;
9378}
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 17559 of file tablecmds.c.

17560{
17561 Relation rel;
17566 ListCell *lc;
17567
17568 /*
17569 * Need lock here in case we are recursing to toast table or index
17570 */
17571 rel = relation_open(tableOid, lockmode);
17572
17573 /* Check first if relation can be moved to new tablespace */
17574 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
17575 {
17577 RelationGetRelid(rel), 0);
17578 relation_close(rel, NoLock);
17579 return;
17580 }
17581
17582 reltoastrelid = rel->rd_rel->reltoastrelid;
17583 /* Fetch the list of indexes on toast relation if necessary */
17585 {
17587
17589 relation_close(toastRel, lockmode);
17590 }
17591
17592 /*
17593 * Relfilenumbers are not unique in databases across tablespaces, so we
17594 * need to allocate a new one in the new tablespace.
17595 */
17596 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
17597 rel->rd_rel->relpersistence);
17598
17599 /* Open old and new relation */
17600 newrlocator = rel->rd_locator;
17602 newrlocator.spcOid = newTableSpace;
17603
17604 /* hand off to AM to actually create new rel storage and copy the data */
17605 if (rel->rd_rel->relkind == RELKIND_INDEX)
17606 {
17608 }
17609 else
17610 {
17611 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
17613 }
17614
17615 /*
17616 * Update the pg_class row.
17617 *
17618 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
17619 * executed on pg_class or its indexes (the above copy wouldn't contain
17620 * the updated pg_class entry), but that's forbidden with
17621 * CheckRelationTableSpaceMove().
17622 */
17623 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
17624
17626
17628
17629 relation_close(rel, NoLock);
17630
17631 /* Make sure the reltablespace change is visible */
17633
17634 /* Move associated toast relation and/or indexes, too */
17636 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
17637 foreach(lc, reltoastidxids)
17638 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
17639
17640 /* Clean up */
17642}
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition catalog.c:584
void RelationAssumeNewRelfilelocator(Relation relation)
Definition relcache.c:3982
Oid RelFileNumber
Definition relpath.h:25
RelFileNumber relNumber
static void table_relation_copy_data(Relation rel, const RelFileLocator *newrlocator)
Definition tableam.h:1717
static void ATExecSetTableSpace(Oid tableOid, Oid newTableSpace, LOCKMODE lockmode)
bool CheckRelationTableSpaceMove(Relation rel, Oid newTableSpaceId)
Definition tablecmds.c:3772
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition tablecmds.c:3829
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 17652 of file tablecmds.c.

17653{
17654 /*
17655 * Shouldn't be called on relations having storage; these are processed in
17656 * phase 3.
17657 */
17658 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
17659
17660 /* check if relation can be moved to its new tablespace */
17661 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
17662 {
17664 RelationGetRelid(rel),
17665 0);
17666 return;
17667 }
17668
17669 /* Update can be done, so change reltablespace */
17670 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
17671
17673
17674 /* Make sure the reltablespace change is visible */
17676}

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

24230{
24234 *listptr2;
24235 bool isSameName = false;
24236 char tmpRelName[NAMEDATALEN];
24237 List *newPartRels = NIL;
24238 List *extDepState = NIL;
24239 ObjectAddress object;
24241 Oid save_userid;
24242 int save_sec_context;
24243 int save_nestlevel;
24245
24247
24248 /*
24249 * Partition is already locked in the transformPartitionCmdForSplit
24250 * function.
24251 */
24253
24255
24256 /* Check descriptions of new partitions. */
24258 {
24260
24261 /* Look up the existing relation by the new partition name. */
24263
24264 /*
24265 * This would fail later on anyway if the relation already exists. But
24266 * by catching it here, we can emit a nicer error message.
24267 */
24269 /* One new partition can have the same name as a split partition. */
24270 isSameName = true;
24271 else if (OidIsValid(existingRelid))
24272 ereport(ERROR,
24274 errmsg("relation \"%s\" already exists", sps->name->relname));
24275 }
24276
24277 /*
24278 * Collect extension dependencies from indexes on the split partition. We
24279 * must do this before detaching it, so we can restore the dependencies on
24280 * the new partitions' indexes later.
24281 */
24283
24286
24287 /* Detach the split partition. */
24289
24290 /*
24291 * Perform a preliminary check to determine whether it's safe to drop the
24292 * split partition before we actually do so later. After merging rows into
24293 * the new partitions via SplitPartitionMoveRows, all old partitions need
24294 * to be dropped. However, since the drop behavior is DROP_RESTRICT and
24295 * the merge process (SplitPartitionMoveRows) can be time-consuming,
24296 * performing an early check on the drop eligibility of old partitions is
24297 * preferable.
24298 */
24299 object.objectId = splitRelOid;
24300 object.classId = RelationRelationId;
24301 object.objectSubId = 0;
24303
24304 /*
24305 * If a new partition has the same name as the split partition, then we
24306 * should rename the split partition to reuse its name.
24307 */
24308 if (isSameName)
24309 {
24310 /*
24311 * We must bump the command counter to make the split partition tuple
24312 * visible for renaming.
24313 */
24315 /* Rename partition. */
24316 sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
24318
24319 /*
24320 * We must bump the command counter to make the split partition tuple
24321 * visible after renaming.
24322 */
24324 }
24325
24326 /* Create new partitions (like a split partition), without indexes. */
24328 {
24330
24332 splitRel->rd_rel->relowner);
24334 }
24335
24336 /*
24337 * Switch to the table owner's userid, so that any index functions are run
24338 * as that user. Also, lockdown security-restricted operations and
24339 * arrange to make GUC variable changes local to this command.
24340 *
24341 * Need to do it after determining the namespace in the
24342 * createPartitionTable() call.
24343 */
24344 GetUserIdAndSecContext(&save_userid, &save_sec_context);
24345 SetUserIdAndSecContext(splitRel->rd_rel->relowner,
24346 save_sec_context | SECURITY_RESTRICTED_OPERATION);
24347 save_nestlevel = NewGUCNestLevel();
24349
24350 /* Copy data from the split partition to the new partitions. */
24352 /* Keep the lock until commit. */
24354
24355 /* Attach new partitions to the partitioned table. */
24357 {
24360
24361 /*
24362 * wqueue = NULL: verification for each cloned constraint is not
24363 * needed.
24364 */
24365 attachPartitionTable(NULL, rel, newPartRel, sps->bound);
24366
24367 /*
24368 * Apply extension dependencies to the new partition's indexes. This
24369 * preserves any "DEPENDS ON EXTENSION" settings from the split
24370 * partition.
24371 */
24373
24374 /* Keep the lock until commit. */
24376 }
24377
24379
24380 /* Drop the split partition. */
24381 object.classId = RelationRelationId;
24382 object.objectId = splitRelOid;
24383 object.objectSubId = 0;
24384 /* Probably DROP_CASCADE is not needed. */
24385 performDeletion(&object, DROP_RESTRICT, 0);
24386
24387 /* Roll back any GUC changes executed by index functions. */
24388 AtEOXact_GUC(false, save_nestlevel);
24389
24390 /* Restore the userid and security context. */
24391 SetUserIdAndSecContext(save_userid, save_sec_context);
24392}
struct RelationData * Relation
Definition genam.h:30
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:550
static void SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel, List *partlist, List *newPartRels)

References applyPartitionIndexExtDeps(), AtEOXact_GUC(), attachPartitionTable(), collectPartitionIndexExtDeps(), CommandCounterIncrement(), createPartitionTable(), detachPartitionTable(), DROP_RESTRICT, ereport, errcode(), errmsg, ERROR, fb(), forboth, foreach_node, freePartitionIndexExtDeps(), get_default_oid_from_partdesc(), GetUserIdAndSecContext(), lappend(), lfirst, list_free(), list_make1_oid, MyProcPid, PartitionCmd::name, NAMEDATALEN, NewGUCNestLevel(), NIL, NoLock, OidIsValid, PartitionCmd::partlist, 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 13442 of file tablecmds.c.

13444{
13446 SysScanDesc scan;
13447 ScanKeyData skey[3];
13448 HeapTuple tuple;
13450 ObjectAddress address;
13451
13453
13454 /*
13455 * Find and check the target constraint
13456 */
13457 ScanKeyInit(&skey[0],
13461 ScanKeyInit(&skey[1],
13465 ScanKeyInit(&skey[2],
13470 true, NULL, 3, skey);
13471
13472 /* There can be at most one matching row */
13473 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
13474 ereport(ERROR,
13476 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13478
13479 con = (Form_pg_constraint) GETSTRUCT(tuple);
13480 if (con->contype != CONSTRAINT_FOREIGN &&
13481 con->contype != CONSTRAINT_CHECK &&
13482 con->contype != CONSTRAINT_NOTNULL)
13483 ereport(ERROR,
13485 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
13487 errdetail("This operation is not supported for this type of constraint."));
13488
13489 if (!con->conenforced)
13490 ereport(ERROR,
13492 errmsg("cannot validate NOT ENFORCED constraint")));
13493
13494 if (!con->convalidated)
13495 {
13496 if (con->contype == CONSTRAINT_FOREIGN)
13497 {
13498 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
13499 tuple, lockmode);
13500 }
13501 else if (con->contype == CONSTRAINT_CHECK)
13502 {
13504 tuple, recurse, recursing, lockmode);
13505 }
13506 else if (con->contype == CONSTRAINT_NOTNULL)
13507 {
13509 tuple, recurse, recursing, lockmode);
13510 }
13511
13512 ObjectAddressSet(address, ConstraintRelationId, con->oid);
13513 }
13514 else
13515 address = InvalidObjectAddress; /* already validated */
13516
13517 systable_endscan(scan);
13518
13520
13521 return address;
13522}
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 6683 of file tablecmds.c.

6684{
6685 Oid relid = RelationGetRelid(rel);
6686 AlteredTableInfo *tab;
6687 ListCell *ltab;
6688
6689 foreach(ltab, *wqueue)
6690 {
6691 tab = (AlteredTableInfo *) lfirst(ltab);
6692 if (tab->relid == relid)
6693 return tab;
6694 }
6695
6696 /*
6697 * Not there, so add it. Note that we make a copy of the relation's
6698 * existing descriptor before anything interesting can happen to it.
6699 */
6701 tab->relid = relid;
6702 tab->rel = NULL; /* set later */
6703 tab->relkind = rel->rd_rel->relkind;
6706 tab->chgAccessMethod = false;
6709 tab->chgPersistence = false;
6710
6711 *wqueue = lappend(*wqueue, tab);
6712
6713 return tab;
6714}
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 5830 of file tablecmds.c.

5833{
5837 List *afterStmts;
5838 ListCell *lc;
5839
5840 /* Gin up an AlterTableStmt with just this subcommand and this table */
5841 atstmt->relation =
5844 -1);
5845 atstmt->relation->inh = recurse;
5846 atstmt->cmds = list_make1(cmd);
5847 atstmt->objtype = OBJECT_TABLE; /* needn't be picky here */
5848 atstmt->missing_ok = false;
5849
5850 /* Transform the AlterTableStmt */
5852 atstmt,
5853 context->queryString,
5854 &beforeStmts,
5855 &afterStmts);
5856
5857 /* Execute any statements that should happen before these subcommand(s) */
5858 foreach(lc, beforeStmts)
5859 {
5860 Node *stmt = (Node *) lfirst(lc);
5861
5864 }
5865
5866 /* Examine the transformed subcommands and schedule them appropriately */
5867 foreach(lc, atstmt->cmds)
5868 {
5871
5872 /*
5873 * This switch need only cover the subcommand types that can be added
5874 * by parse_utilcmd.c; otherwise, we'll use the default strategy of
5875 * executing the subcommand immediately, as a substitute for the
5876 * original subcommand. (Note, however, that this does cause
5877 * AT_AddConstraint subcommands to be rescheduled into later passes,
5878 * which is important for index and foreign key constraints.)
5879 *
5880 * We assume we needn't do any phase-1 checks for added subcommands.
5881 */
5882 switch (cmd2->subtype)
5883 {
5884 case AT_AddIndex:
5886 break;
5889 break;
5890 case AT_AddConstraint:
5891 /* Recursion occurs during execution phase */
5892 if (recurse)
5893 cmd2->recurse = true;
5894 switch (castNode(Constraint, cmd2->def)->contype)
5895 {
5896 case CONSTR_NOTNULL:
5898 break;
5899 case CONSTR_PRIMARY:
5900 case CONSTR_UNIQUE:
5901 case CONSTR_EXCLUSION:
5903 break;
5904 default:
5906 break;
5907 }
5908 break;
5910 /* This command never recurses */
5911 /* No command-specific prep needed */
5913 break;
5914 default:
5915 pass = cur_pass;
5916 break;
5917 }
5918
5919 if (pass < cur_pass)
5920 {
5921 /* Cannot schedule into a pass we already finished */
5922 elog(ERROR, "ALTER TABLE scheduling failure: too late for pass %d",
5923 pass);
5924 }
5925 else if (pass > cur_pass)
5926 {
5927 /* OK, queue it up for later */
5928 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd2);
5929 }
5930 else
5931 {
5932 /*
5933 * We should see at most one subcommand for the current pass,
5934 * which is the transformed version of the original subcommand.
5935 */
5936 if (newcmd == NULL && cmd->subtype == cmd2->subtype)
5937 {
5938 /* Found the transformed version of our subcommand */
5939 newcmd = cmd2;
5940 }
5941 else
5942 elog(ERROR, "ALTER TABLE scheduling failure: bogus item for pass %d",
5943 pass);
5944 }
5945 }
5946
5947 /* Queue up any after-statements to happen at the end */
5948 tab->afterStmts = list_concat(tab->afterStmts, afterStmts);
5949
5950 return newcmd;
5951}
List * list_concat(List *list1, const List *list2)
Definition list.c:561
AlterTableStmt * transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, const char *queryString, List **beforeStmts, List **afterStmts)
List * subcmds[AT_NUM_PASSES]
Definition tablecmds.c:189
void ProcessUtilityForAlterTable(Node *stmt, AlterTableUtilityContext *context)
Definition utility.c:1971

References AlteredTableInfo::afterStmts, AT_AddConstraint, AT_AddIndex, AT_AddIndexConstraint, AT_AlterColumnGenericOptions, AT_PASS_ADD_INDEX, AT_PASS_ADD_INDEXCONSTR, AT_PASS_ADD_OTHERCONSTR, AT_PASS_COL_ATTRS, AT_PASS_MISC, castNode, CommandCounterIncrement(), CONSTR_EXCLUSION, CONSTR_NOTNULL, CONSTR_PRIMARY, CONSTR_UNIQUE, elog, ERROR, fb(), get_namespace_name(), lappend(), lfirst, lfirst_node, list_concat(), list_make1, makeNode, makeRangeVar(), OBJECT_TABLE, ProcessUtilityForAlterTable(), pstrdup(), AlterTableUtilityContext::queryString, RelationGetNamespace, RelationGetRelationName, RelationGetRelid, stmt, AlteredTableInfo::subcmds, AlterTableCmd::subtype, and transformAlterTableStmt().

Referenced by ATExecAddColumn(), ATExecCmd(), and ATPrepCmd().

◆ ATPostAlterTypeCleanup()

static void ATPostAlterTypeCleanup ( List **  wqueue,
AlteredTableInfo tab,
LOCKMODE  lockmode 
)
static

Definition at line 16141 of file tablecmds.c.

16142{
16143 ObjectAddress obj;
16144 ObjectAddresses *objects;
16147
16148 /*
16149 * Collect all the constraints and indexes to drop so we can process them
16150 * in a single call. That way we don't have to worry about dependencies
16151 * among them.
16152 */
16153 objects = new_object_addresses();
16154
16155 /*
16156 * Re-parse the index and constraint definitions, and attach them to the
16157 * appropriate work queue entries. We do this before dropping because in
16158 * the case of a constraint on another table, we might not yet have
16159 * exclusive lock on the table the constraint is attached to, and we need
16160 * to get that before reparsing/dropping. (That's possible at least for
16161 * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
16162 * requires a dependency on the target table's composite type in the other
16163 * table's constraint expressions.)
16164 *
16165 * We can't rely on the output of deparsing to tell us which relation to
16166 * operate on, because concurrent activity might have made the name
16167 * resolve differently. Instead, we've got to use the OID of the
16168 * constraint or index we're processing to figure out which relation to
16169 * operate on.
16170 */
16173 {
16175 HeapTuple tup;
16177 Oid relid;
16178 Oid confrelid;
16179 bool conislocal;
16180
16182 if (!HeapTupleIsValid(tup)) /* should not happen */
16183 elog(ERROR, "cache lookup failed for constraint %u", oldId);
16185 if (OidIsValid(con->conrelid))
16186 relid = con->conrelid;
16187 else
16188 {
16189 /* must be a domain constraint */
16190 relid = get_typ_typrelid(getBaseType(con->contypid));
16191 if (!OidIsValid(relid))
16192 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
16193 }
16194 confrelid = con->confrelid;
16195 conislocal = con->conislocal;
16197
16199 add_exact_object_address(&obj, objects);
16200
16201 /*
16202 * If the constraint is inherited (only), we don't want to inject a
16203 * new definition here; it'll get recreated when
16204 * ATAddCheckNNConstraint recurses from adding the parent table's
16205 * constraint. But we had to carry the info this far so that we can
16206 * drop the constraint below.
16207 */
16208 if (!conislocal)
16209 continue;
16210
16211 /*
16212 * When rebuilding another table's constraint that references the
16213 * table we're modifying, we might not yet have any lock on the other
16214 * table, so get one now. We'll need AccessExclusiveLock for the DROP
16215 * CONSTRAINT step, so there's no value in asking for anything weaker.
16216 */
16217 if (relid != tab->relid)
16219
16220 ATPostAlterTypeParse(oldId, relid, confrelid,
16221 (char *) lfirst(def_item),
16222 wqueue, lockmode, tab->rewrite);
16223 }
16226 {
16228 Oid relid;
16229
16230 relid = IndexGetRelation(oldId, false);
16231
16232 /*
16233 * As above, make sure we have lock on the index's table if it's not
16234 * the same table.
16235 */
16236 if (relid != tab->relid)
16238
16240 (char *) lfirst(def_item),
16241 wqueue, lockmode, tab->rewrite);
16242
16244 add_exact_object_address(&obj, objects);
16245 }
16246
16247 /* add dependencies for new statistics */
16250 {
16252 Oid relid;
16253
16254 relid = StatisticsGetRelation(oldId, false);
16255
16256 /*
16257 * As above, make sure we have lock on the statistics object's table
16258 * if it's not the same table. However, we take
16259 * ShareUpdateExclusiveLock here, aligning with the lock level used in
16260 * CreateStatistics and RemoveStatisticsById.
16261 *
16262 * CAUTION: this should be done after all cases that grab
16263 * AccessExclusiveLock, else we risk causing deadlock due to needing
16264 * to promote our table lock.
16265 */
16266 if (relid != tab->relid)
16268
16270 (char *) lfirst(def_item),
16271 wqueue, lockmode, tab->rewrite);
16272
16274 add_exact_object_address(&obj, objects);
16275 }
16276
16277 /*
16278 * Queue up command to restore replica identity index marking
16279 */
16280 if (tab->replicaIdentityIndex)
16281 {
16284
16285 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
16286 subcmd->name = tab->replicaIdentityIndex;
16288 cmd->def = (Node *) subcmd;
16289
16290 /* do it after indexes and constraints */
16293 }
16294
16295 /*
16296 * Queue up command to restore marking of index used for cluster.
16297 */
16298 if (tab->clusterOnIndex)
16299 {
16301
16302 cmd->subtype = AT_ClusterOn;
16303 cmd->name = tab->clusterOnIndex;
16304
16305 /* do it after indexes and constraints */
16308 }
16309
16310 /*
16311 * It should be okay to use DROP_RESTRICT here, since nothing else should
16312 * be depending on these objects.
16313 */
16315
16316 free_object_addresses(objects);
16317
16318 /*
16319 * The objects will get recreated during subsequent passes over the work
16320 * queue.
16321 */
16322}
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition index.c:3616
Oid get_typ_typrelid(Oid typid)
Definition lsyscache.c:3047
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition statscmds.c:968
List * changedConstraintDefs
Definition tablecmds.c:207
List * changedStatisticsDefs
Definition tablecmds.c:213
char * clusterOnIndex
Definition tablecmds.c:211
char * replicaIdentityIndex
Definition tablecmds.c:210
List * changedStatisticsOids
Definition tablecmds.c:212
List * changedIndexDefs
Definition tablecmds.c:209
List * changedIndexOids
Definition tablecmds.c:208
List * changedConstraintOids
Definition tablecmds.c:206
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)

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

Referenced by ATRewriteCatalogs().

◆ ATPostAlterTypeParse()

static void ATPostAlterTypeParse ( Oid  oldId,
Oid  oldRelId,
Oid  refRelId,
char cmd,
List **  wqueue,
LOCKMODE  lockmode,
bool  rewrite 
)
static

Definition at line 16333 of file tablecmds.c.

16335{
16339 Relation rel;
16340
16341 /*
16342 * We expect that we will get only ALTER TABLE and CREATE INDEX
16343 * statements. Hence, there is no need to pass them through
16344 * parse_analyze_*() or the rewriter, but instead we need to pass them
16345 * through parse_utilcmd.c to make them ready for execution.
16346 */
16350 {
16352 Node *stmt = rs->stmt;
16353
16354 if (IsA(stmt, IndexStmt))
16357 (IndexStmt *) stmt,
16358 cmd));
16359 else if (IsA(stmt, AlterTableStmt))
16360 {
16362 List *afterStmts;
16363
16365 (AlterTableStmt *) stmt,
16366 cmd,
16367 &beforeStmts,
16368 &afterStmts);
16372 }
16373 else if (IsA(stmt, CreateStatsStmt))
16377 cmd));
16378 else
16380 }
16381
16382 /* Caller should already have acquired whatever lock we need. */
16384
16385 /*
16386 * Attach each generated command to the proper place in the work queue.
16387 * Note this could result in creation of entirely new work-queue entries.
16388 *
16389 * Also note that we have to tweak the command subtypes, because it turns
16390 * out that re-creation of indexes and constraints has to act a bit
16391 * differently from initial creation.
16392 */
16393 foreach(list_item, querytree_list)
16394 {
16395 Node *stm = (Node *) lfirst(list_item);
16396 AlteredTableInfo *tab;
16397
16398 tab = ATGetQueueEntry(wqueue, rel);
16399
16400 if (IsA(stm, IndexStmt))
16401 {
16402 IndexStmt *stmt = (IndexStmt *) stm;
16404
16405 if (!rewrite)
16407 stmt->reset_default_tblspc = true;
16408 /* keep the index's comment */
16409 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
16410
16412 newcmd->subtype = AT_ReAddIndex;
16413 newcmd->def = (Node *) stmt;
16416 }
16417 else if (IsA(stm, AlterTableStmt))
16418 {
16420 ListCell *lcmd;
16421
16422 foreach(lcmd, stmt->cmds)
16423 {
16425
16426 if (cmd->subtype == AT_AddIndex)
16427 {
16429 Oid indoid;
16430
16431 indstmt = castNode(IndexStmt, cmd->def);
16432 indoid = get_constraint_index(oldId);
16433
16434 if (!rewrite)
16435 TryReuseIndex(indoid, indstmt);
16436 /* keep any comment on the index */
16437 indstmt->idxcomment = GetComment(indoid,
16439 indstmt->reset_default_tblspc = true;
16440
16441 cmd->subtype = AT_ReAddIndex;
16443 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
16444
16445 /* recreate any comment on the constraint */
16448 oldId,
16449 rel,
16450 NIL,
16451 indstmt->idxname);
16452 }
16453 else if (cmd->subtype == AT_AddConstraint)
16454 {
16455 Constraint *con = castNode(Constraint, cmd->def);
16456
16458 /* rewriting neither side of a FK */
16459 if (con->contype == CONSTR_FOREIGN &&
16460 !rewrite && tab->rewrite == 0)
16462 con->reset_default_tblspc = true;
16466
16467 /*
16468 * Recreate any comment on the constraint. If we have
16469 * recreated a primary key, then transformTableConstraint
16470 * has added an unnamed not-null constraint here; skip
16471 * this in that case.
16472 */
16473 if (con->conname)
16476 oldId,
16477 rel,
16478 NIL,
16479 con->conname);
16480 else
16481 Assert(con->contype == CONSTR_NOTNULL);
16482 }
16483 else
16484 elog(ERROR, "unexpected statement subtype: %d",
16485 (int) cmd->subtype);
16486 }
16487 }
16488 else if (IsA(stm, AlterDomainStmt))
16489 {
16491
16492 if (stmt->subtype == AD_AddConstraint)
16493 {
16494 Constraint *con = castNode(Constraint, stmt->def);
16496
16498 cmd->def = (Node *) stmt;
16501
16502 /* recreate any comment on the constraint */
16505 oldId,
16506 NULL,
16507 stmt->typeName,
16508 con->conname);
16509 }
16510 else
16511 elog(ERROR, "unexpected statement subtype: %d",
16512 (int) stmt->subtype);
16513 }
16514 else if (IsA(stm, CreateStatsStmt))
16515 {
16518
16519 /* keep the statistics object's comment */
16520 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
16521
16523 newcmd->subtype = AT_ReAddStatistics;
16524 newcmd->def = (Node *) stmt;
16525 tab->subcmds[AT_PASS_MISC] =
16527 }
16528 else
16529 elog(ERROR, "unexpected statement type: %d",
16530 (int) nodeTag(stm));
16531 }
16532
16533 relation_close(rel, NoLock);
16534}
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:1339
#define nodeTag(nodeptr)
Definition nodes.h:137
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 7334 of file tablecmds.c.

7337{
7338 if (rel->rd_rel->reloftype && !recursing)
7339 ereport(ERROR,
7341 errmsg("cannot add column to typed table")));
7342
7343 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7344 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7345
7346 if (recurse && !is_view)
7347 cmd->recurse = true;
7348}
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:7032

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

9637{
9639 List *children = NIL;
9640 bool got_children = false;
9641
9643 if (pkconstr->contype != CONSTR_PRIMARY)
9644 return;
9645
9646 /* Verify that columns are not-null, or request that they be made so */
9648 {
9651 HeapTuple tuple;
9652
9653 /*
9654 * First check if a suitable constraint exists. If it does, we don't
9655 * need to request another one. We do need to bail out if it's not
9656 * valid, though.
9657 */
9659 if (tuple != NULL)
9660 {
9662
9663 /* All good with this one; don't request another */
9664 heap_freetuple(tuple);
9665 continue;
9666 }
9667 else if (!recurse)
9668 {
9669 /*
9670 * No constraint on this column. Asked not to recurse, we won't
9671 * create one here, but verify that all children have one.
9672 */
9673 if (!got_children)
9674 {
9676 lockmode);
9677 /* only search for children on the first time through */
9678 got_children = true;
9679 }
9680
9681 foreach_oid(childrelid, children)
9682 {
9683 HeapTuple tup;
9684
9686 if (!tup)
9687 ereport(ERROR,
9688 errmsg("column \"%s\" of table \"%s\" is not marked NOT NULL",
9690 /* verify it's good enough */
9692 }
9693 }
9694
9695 /* This column is not already not-null, so add it to the queue */
9697
9699 newcmd->subtype = AT_AddConstraint;
9700 /* note we force recurse=true here; see above */
9701 newcmd->recurse = true;
9702 newcmd->def = (Node *) nnconstr;
9703
9704 ATPrepCmd(wqueue, rel, newcmd, true, false, lockmode, context);
9705 }
9706}
Definition value.h:64
static void verifyNotNullPKCompatible(HeapTuple tuple, const char *colname)
Definition tablecmds.c:9713

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

14915{
14916 char *colName = cmd->name;
14917 ColumnDef *def = (ColumnDef *) cmd->def;
14918 TypeName *typeName = def->typeName;
14919 Node *transform = def->cooked_default;
14920 HeapTuple tuple;
14923 Oid targettype;
14924 int32 targettypmod;
14927 ParseState *pstate = make_parsestate(NULL);
14929 bool is_expr;
14930
14931 pstate->p_sourcetext = context->queryString;
14932
14933 if (rel->rd_rel->reloftype && !recursing)
14934 ereport(ERROR,
14936 errmsg("cannot alter column type of typed table"),
14937 parser_errposition(pstate, def->location)));
14938
14939 /* lookup the attribute so we can check inheritance status */
14941 if (!HeapTupleIsValid(tuple))
14942 ereport(ERROR,
14944 errmsg("column \"%s\" of relation \"%s\" does not exist",
14946 parser_errposition(pstate, def->location)));
14948 attnum = attTup->attnum;
14949
14950 /* Can't alter a system attribute */
14951 if (attnum <= 0)
14952 ereport(ERROR,
14954 errmsg("cannot alter system column \"%s\"", colName),
14955 parser_errposition(pstate, def->location)));
14956
14957 /*
14958 * Cannot specify USING when altering type of a generated column, because
14959 * that would violate the generation expression.
14960 */
14961 if (attTup->attgenerated && def->cooked_default)
14962 ereport(ERROR,
14964 errmsg("cannot specify USING when altering type of generated column"),
14965 errdetail("Column \"%s\" is a generated column.", colName),
14966 parser_errposition(pstate, def->location)));
14967
14968 /*
14969 * Don't alter inherited columns. At outer level, there had better not be
14970 * any inherited definition; when recursing, we assume this was checked at
14971 * the parent level (see below).
14972 */
14973 if (attTup->attinhcount > 0 && !recursing)
14974 ereport(ERROR,
14976 errmsg("cannot alter inherited column \"%s\"", colName),
14977 parser_errposition(pstate, def->location)));
14978
14979 /* Don't alter columns used in the partition key */
14980 if (has_partition_attrs(rel,
14982 &is_expr))
14983 ereport(ERROR,
14985 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14987 parser_errposition(pstate, def->location)));
14988
14989 /* Look up the target type */
14990 typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14991
14993 if (aclresult != ACLCHECK_OK)
14994 aclcheck_error_type(aclresult, targettype);
14995
14996 /* And the collation */
14997 targetcollid = GetColumnDefCollation(pstate, def, targettype);
14998
14999 /* make sure datatype is legal for a column */
15001 list_make1_oid(rel->rd_rel->reltype),
15002 (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
15003
15004 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
15005 {
15006 /* do nothing */
15007 }
15008 else if (tab->relkind == RELKIND_RELATION ||
15010 {
15011 /*
15012 * Set up an expression to transform the old data value to the new
15013 * type. If a USING option was given, use the expression as
15014 * transformed by transformAlterTableStmt, else just take the old
15015 * value and try to coerce it. We do this first so that type
15016 * incompatibility can be detected before we waste effort, and because
15017 * we need the expression to be parsed against the original table row
15018 * type.
15019 */
15020 if (!transform)
15021 {
15022 transform = (Node *) makeVar(1, attnum,
15023 attTup->atttypid, attTup->atttypmod,
15024 attTup->attcollation,
15025 0);
15026 }
15027
15028 transform = coerce_to_target_type(pstate,
15029 transform, exprType(transform),
15030 targettype, targettypmod,
15033 -1);
15034 if (transform == NULL)
15035 {
15036 /* error text depends on whether USING was specified or not */
15037 if (def->cooked_default != NULL)
15038 ereport(ERROR,
15040 errmsg("result of USING clause for column \"%s\""
15041 " cannot be cast automatically to type %s",
15042 colName, format_type_be(targettype)),
15043 errhint("You might need to add an explicit cast.")));
15044 else
15045 ereport(ERROR,
15047 errmsg("column \"%s\" cannot be cast automatically to type %s",
15048 colName, format_type_be(targettype)),
15049 !attTup->attgenerated ?
15050 /* translator: USING is SQL, don't translate it */
15051 errhint("You might need to specify \"USING %s::%s\".",
15053 format_type_with_typemod(targettype,
15054 targettypmod)) : 0));
15055 }
15056
15057 /* Fix collations after all else */
15058 assign_expr_collations(pstate, transform);
15059
15060 /* Expand virtual generated columns in the expr. */
15061 transform = expand_generated_columns_in_expr(transform, rel, 1);
15062
15063 /* Plan the expr now so we can accurately assess the need to rewrite. */
15064 transform = (Node *) expression_planner((Expr *) transform);
15065
15066 /*
15067 * Add a work queue item to make ATRewriteTable update the column
15068 * contents.
15069 */
15071 newval->attnum = attnum;
15072 newval->expr = (Expr *) transform;
15073 newval->is_generated = false;
15074
15075 tab->newvals = lappend(tab->newvals, newval);
15076 if (ATColumnChangeRequiresRewrite(transform, attnum))
15078 }
15079 else if (transform)
15080 ereport(ERROR,
15082 errmsg("\"%s\" is not a table",
15084
15085 if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
15086 {
15087 /*
15088 * For relations or columns without storage, do this check now.
15089 * Regular tables will check it later when the table is being
15090 * rewritten.
15091 */
15092 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
15093 }
15094
15095 ReleaseSysCache(tuple);
15096
15097 /*
15098 * Recurse manually by queueing a new command for each child, if
15099 * necessary. We cannot apply ATSimpleRecursion here because we need to
15100 * remap attribute numbers in the USING expression, if any.
15101 *
15102 * If we are told not to recurse, there had better not be any child
15103 * tables; else the alter would put them out of step.
15104 */
15105 if (recurse)
15106 {
15107 Oid relid = RelationGetRelid(rel);
15110 ListCell *lo,
15111 *li;
15112
15113 child_oids = find_all_inheritors(relid, lockmode,
15115
15116 /*
15117 * find_all_inheritors does the recursive search of the inheritance
15118 * hierarchy, so all we have to do is process all of the relids in the
15119 * list that it returns.
15120 */
15122 {
15124 int numparents = lfirst_int(li);
15128
15129 if (childrelid == relid)
15130 continue;
15131
15132 /* find_all_inheritors already got lock */
15135
15136 /*
15137 * Verify that the child doesn't have any inherited definitions of
15138 * this column that came from outside this inheritance hierarchy.
15139 * (renameatt makes a similar test, though in a different way
15140 * because of its different recursion mechanism.)
15141 */
15143 colName);
15145 ereport(ERROR,
15147 errmsg("column \"%s\" of relation \"%s\" does not exist",
15150
15151 if (childattTup->attinhcount > numparents)
15152 ereport(ERROR,
15154 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
15156
15158
15159 /*
15160 * Remap the attribute numbers. If no USING expression was
15161 * specified, there is no need for this step.
15162 */
15163 if (def->cooked_default)
15164 {
15165 AttrMap *attmap;
15166 bool found_whole_row;
15167
15168 /* create a copy to scribble on */
15169 cmd = copyObject(cmd);
15170
15172 RelationGetDescr(rel),
15173 false);
15174 ((ColumnDef *) cmd->def)->cooked_default =
15175 map_variable_attnos(def->cooked_default,
15176 1, 0,
15177 attmap,
15178 InvalidOid, &found_whole_row);
15179 if (found_whole_row)
15180 ereport(ERROR,
15182 errmsg("cannot convert whole-row table reference"),
15183 errdetail("USING expression contains a whole-row table reference.")));
15184 pfree(attmap);
15185 }
15186 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
15188 }
15189 }
15190 else if (!recursing &&
15192 ereport(ERROR,
15194 errmsg("type of inherited column \"%s\" must be changed in child tables too",
15195 colName)));
15196
15197 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
15198 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
15199}
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:7077

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

17950{
17951 if (child_rel->rd_rel->reloftype)
17952 ereport(ERROR,
17954 errmsg("cannot change inheritance of typed table")));
17955
17956 if (child_rel->rd_rel->relispartition)
17957 ereport(ERROR,
17959 errmsg("cannot change inheritance of a partition")));
17960}

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

Referenced by ATPrepCmd().

◆ ATPrepChangePersistence()

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

Definition at line 19524 of file tablecmds.c.

19525{
19527 HeapTuple tuple;
19528 SysScanDesc scan;
19529 ScanKeyData skey[1];
19530
19531 /*
19532 * Disallow changing status for a temp table. Also verify whether we can
19533 * get away with doing nothing; in such cases we don't need to run the
19534 * checks below, either.
19535 */
19536 switch (rel->rd_rel->relpersistence)
19537 {
19539 ereport(ERROR,
19541 errmsg("cannot change logged status of table \"%s\" because it is temporary",
19543 errtable(rel)));
19544 break;
19546 if (toLogged)
19547 /* nothing to do */
19548 return;
19549 break;
19551 if (!toLogged)
19552 /* nothing to do */
19553 return;
19554 break;
19555 }
19556
19557 /*
19558 * Check that the table is not part of any publication when changing to
19559 * UNLOGGED, as UNLOGGED tables can't be published.
19560 */
19561 if (!toLogged &&
19563 ereport(ERROR,
19565 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
19567 errdetail("Unlogged relations cannot be replicated.")));
19568
19569 /*
19570 * Check existing foreign key constraints to preserve the invariant that
19571 * permanent tables cannot reference unlogged ones. Self-referencing
19572 * foreign keys can safely be ignored.
19573 */
19575
19576 /*
19577 * Scan conrelid if changing to permanent, else confrelid. This also
19578 * determines whether a useful index exists.
19579 */
19580 ScanKeyInit(&skey[0],
19587 true, NULL, 1, skey);
19588
19589 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19590 {
19592
19593 if (con->contype == CONSTRAINT_FOREIGN)
19594 {
19596 Relation foreignrel;
19597
19598 /* the opposite end of what we used as scankey */
19599 foreignrelid = toLogged ? con->confrelid : con->conrelid;
19600
19601 /* ignore if self-referencing */
19602 if (RelationGetRelid(rel) == foreignrelid)
19603 continue;
19604
19606
19607 if (toLogged)
19608 {
19609 if (!RelationIsPermanent(foreignrel))
19610 ereport(ERROR,
19612 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
19614 RelationGetRelationName(foreignrel)),
19615 errtableconstraint(rel, NameStr(con->conname))));
19616 }
19617 else
19618 {
19619 if (RelationIsPermanent(foreignrel))
19620 ereport(ERROR,
19622 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
19624 RelationGetRelationName(foreignrel)),
19625 errtableconstraint(rel, NameStr(con->conname))));
19626 }
19627
19628 relation_close(foreignrel, AccessShareLock);
19629 }
19630 }
19631
19632 systable_endscan(scan);
19633
19635
19636 /* force rewrite if necessary; see comment in ATRewriteTables */
19638 if (toLogged)
19640 else
19642 tab->chgPersistence = true;
19643}
#define AT_REWRITE_ALTER_PERSISTENCE
return true
Definition isn.c:130
int errtableconstraint(Relation rel, const char *conname)
Definition relcache.c:6138
int errtable(Relation rel)
Definition relcache.c:6084

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

5005{
5006 AlteredTableInfo *tab;
5008
5009 /* Find or create work queue entry for this table */
5010 tab = ATGetQueueEntry(wqueue, rel);
5011
5012 /*
5013 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
5014 * partitions that are pending detach.
5015 */
5016 if (rel->rd_rel->relispartition &&
5019 ereport(ERROR,
5021 errmsg("cannot alter partition \"%s\" with an incomplete detach",
5023 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
5024
5025 /*
5026 * Copy the original subcommand for each table, so we can scribble on it.
5027 * This avoids conflicts when different child tables need to make
5028 * different parse transformations (for example, the same column may have
5029 * different column numbers in different children).
5030 */
5031 cmd = copyObject(cmd);
5032
5033 /*
5034 * Do permissions and relkind checking, recursion to child tables if
5035 * needed, and any additional phase-1 processing needed. (But beware of
5036 * adding any processing that looks at table details that another
5037 * subcommand could change. In some cases we reject multiple subcommands
5038 * that could try to change the same state in contrary ways.)
5039 */
5040 switch (cmd->subtype)
5041 {
5042 case AT_AddColumn: /* ADD COLUMN */
5043 ATSimplePermissions(cmd->subtype, rel,
5046 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
5047 lockmode, context);
5048 /* Recursion occurs during execution phase */
5050 break;
5051 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5053 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
5054 lockmode, context);
5055 /* Recursion occurs during execution phase */
5057 break;
5058 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5059
5060 /*
5061 * We allow defaults on views so that INSERT into a view can have
5062 * default-ish behavior. This works because the rewriter
5063 * substitutes default values into INSERTs before it expands
5064 * rules.
5065 */
5066 ATSimplePermissions(cmd->subtype, rel,
5069 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5070 /* No command-specific prep needed */
5072 break;
5073 case AT_CookedColumnDefault: /* add a pre-cooked default */
5074 /* This is currently used only in CREATE TABLE */
5075 /* (so the permission check really isn't necessary) */
5076 ATSimplePermissions(cmd->subtype, rel,
5078 /* This command never recurses */
5080 break;
5081 case AT_AddIdentity:
5082 ATSimplePermissions(cmd->subtype, rel,
5085 /* Set up recursion for phase 2; no other prep needed */
5086 if (recurse)
5087 cmd->recurse = true;
5089 break;
5090 case AT_SetIdentity:
5091 ATSimplePermissions(cmd->subtype, rel,
5094 /* Set up recursion for phase 2; no other prep needed */
5095 if (recurse)
5096 cmd->recurse = true;
5097 /* This should run after AddIdentity, so do it in MISC pass */
5099 break;
5100 case AT_DropIdentity:
5101 ATSimplePermissions(cmd->subtype, rel,
5104 /* Set up recursion for phase 2; no other prep needed */
5105 if (recurse)
5106 cmd->recurse = true;
5108 break;
5109 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5110 ATSimplePermissions(cmd->subtype, rel,
5112 /* Set up recursion for phase 2; no other prep needed */
5113 if (recurse)
5114 cmd->recurse = true;
5116 break;
5117 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5118 ATSimplePermissions(cmd->subtype, rel,
5120 /* Set up recursion for phase 2; no other prep needed */
5121 if (recurse)
5122 cmd->recurse = true;
5124 break;
5125 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5126 ATSimplePermissions(cmd->subtype, rel,
5128 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5130 break;
5131 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5132 ATSimplePermissions(cmd->subtype, rel,
5134 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5135 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5137 break;
5138 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5139 ATSimplePermissions(cmd->subtype, rel,
5142 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5143 /* No command-specific prep needed */
5145 break;
5146 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5147 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5148 ATSimplePermissions(cmd->subtype, rel,
5151 /* This command never recurses */
5153 break;
5154 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5155 ATSimplePermissions(cmd->subtype, rel,
5158 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5159 /* No command-specific prep needed */
5161 break;
5162 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5163 ATSimplePermissions(cmd->subtype, rel,
5165 /* This command never recurses */
5166 /* No command-specific prep needed */
5168 break;
5169 case AT_DropColumn: /* DROP COLUMN */
5170 ATSimplePermissions(cmd->subtype, rel,
5173 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5174 lockmode, context);
5175 /* Recursion occurs during execution phase */
5177 break;
5178 case AT_AddIndex: /* ADD INDEX */
5180 /* This command never recurses */
5181 /* No command-specific prep needed */
5183 break;
5184 case AT_AddConstraint: /* ADD CONSTRAINT */
5185 ATSimplePermissions(cmd->subtype, rel,
5187 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5188 if (recurse)
5189 {
5190 /* recurses at exec time; lock descendants and set flag */
5191 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5192 cmd->recurse = true;
5193 }
5195 break;
5196 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5198 /* This command never recurses */
5199 /* No command-specific prep needed */
5201 break;
5202 case AT_DropConstraint: /* DROP CONSTRAINT */
5203 ATSimplePermissions(cmd->subtype, rel,
5205 ATCheckPartitionsNotInUse(rel, lockmode);
5206 /* Other recursion occurs during execution phase */
5207 /* No command-specific prep needed except saving recurse flag */
5208 if (recurse)
5209 cmd->recurse = true;
5211 break;
5212 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5213 ATSimplePermissions(cmd->subtype, rel,
5216 /* See comments for ATPrepAlterColumnType */
5217 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5218 AT_PASS_UNSET, context);
5219 Assert(cmd != NULL);
5220 /* Performs own recursion */
5221 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5222 lockmode, context);
5224 break;
5227 /* This command never recurses */
5228 /* No command-specific prep needed */
5230 break;
5231 case AT_ChangeOwner: /* ALTER OWNER */
5232 /* This command never recurses */
5233 /* No command-specific prep needed */
5235 break;
5236 case AT_ClusterOn: /* CLUSTER ON */
5237 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5238 ATSimplePermissions(cmd->subtype, rel,
5240 /* These commands never recurse */
5241 /* No command-specific prep needed */
5243 break;
5244 case AT_SetLogged: /* SET LOGGED */
5245 case AT_SetUnLogged: /* SET UNLOGGED */
5247 if (tab->chgPersistence)
5248 ereport(ERROR,
5250 errmsg("cannot change persistence setting twice")));
5251 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5253 break;
5254 case AT_DropOids: /* SET WITHOUT OIDS */
5255 ATSimplePermissions(cmd->subtype, rel,
5258 break;
5259 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5260 ATSimplePermissions(cmd->subtype, rel,
5262
5263 /* check if another access method change was already requested */
5264 if (tab->chgAccessMethod)
5265 ereport(ERROR,
5267 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5268
5269 ATPrepSetAccessMethod(tab, rel, cmd->name);
5270 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5271 break;
5272 case AT_SetTableSpace: /* SET TABLESPACE */
5275 /* This command never recurses */
5276 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5277 pass = AT_PASS_MISC; /* doesn't actually matter */
5278 break;
5279 case AT_SetRelOptions: /* SET (...) */
5280 case AT_ResetRelOptions: /* RESET (...) */
5281 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5282 ATSimplePermissions(cmd->subtype, rel,
5285 /* This command never recurses */
5286 /* No command-specific prep needed */
5288 break;
5289 case AT_AddInherit: /* INHERIT */
5290 ATSimplePermissions(cmd->subtype, rel,
5292 /* This command never recurses */
5295 break;
5296 case AT_DropInherit: /* NO INHERIT */
5297 ATSimplePermissions(cmd->subtype, rel,
5299 /* This command never recurses */
5302 break;
5303 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5304 ATSimplePermissions(cmd->subtype, rel,
5306 /* Recursion occurs during execution phase */
5307 if (recurse)
5308 cmd->recurse = true;
5310 break;
5311 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5312 ATSimplePermissions(cmd->subtype, rel,
5314 /* Recursion occurs during execution phase */
5315 /* No command-specific prep needed except saving recurse flag */
5316 if (recurse)
5317 cmd->recurse = true;
5319 break;
5320 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5321 ATSimplePermissions(cmd->subtype, rel,
5324 /* This command never recurses */
5325 /* No command-specific prep needed */
5326 break;
5327 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5330 case AT_EnableTrigAll:
5331 case AT_EnableTrigUser:
5332 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5333 case AT_DisableTrigAll:
5334 case AT_DisableTrigUser:
5335 ATSimplePermissions(cmd->subtype, rel,
5337 /* Set up recursion for phase 2; no other prep needed */
5338 if (recurse)
5339 cmd->recurse = true;
5341 break;
5342 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5345 case AT_DisableRule:
5346 case AT_AddOf: /* OF */
5347 case AT_DropOf: /* NOT OF */
5352 ATSimplePermissions(cmd->subtype, rel,
5354 /* These commands never recurse */
5355 /* No command-specific prep needed */
5357 break;
5358 case AT_GenericOptions:
5360 /* No command-specific prep needed */
5362 break;
5363 case AT_AttachPartition:
5364 ATSimplePermissions(cmd->subtype, rel,
5366 /* No command-specific prep needed */
5368 break;
5369 case AT_DetachPartition:
5371 /* No command-specific prep needed */
5373 break;
5376 /* No command-specific prep needed */
5378 break;
5379 case AT_MergePartitions:
5380 case AT_SplitPartition:
5382 /* No command-specific prep needed */
5384 break;
5385 default: /* oops */
5386 elog(ERROR, "unrecognized alter table type: %d",
5387 (int) cmd->subtype);
5388 pass = AT_PASS_UNSET; /* keep compiler quiet */
5389 break;
5390 }
5392
5393 /* Add the subcommand to the appropriate list for phase 2 */
5394 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5395}
bool PartitionHasPendingDetach(Oid partoid)
static void ATPrepChangeInherit(Relation child_rel)
#define ATT_SEQUENCE
Definition tablecmds.c:345
static void ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacename, LOCKMODE lockmode)
static void ATCheckPartitionsNotInUse(Relation rel, LOCKMODE lockmode)
Definition tablecmds.c:7002
#define ATT_INDEX
Definition tablecmds.c:341
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition tablecmds.c:8891
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:9391
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:7334
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:6957
#define ATT_PARTITIONED_INDEX
Definition tablecmds.c:344
static void ATPrepChangePersistence(AlteredTableInfo *tab, Relation rel, bool toLogged)
static void ATPrepAddPrimaryKey(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:9634
#define ATT_VIEW
Definition tablecmds.c:339
static void ATPrepAlterColumnType(List **wqueue, AlteredTableInfo *tab, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
#define ATT_COMPOSITE_TYPE
Definition tablecmds.c:342
#define ATT_MATVIEW
Definition tablecmds.c:340
static void ATPrepSetAccessMethod(AlteredTableInfo *tab, Relation rel, const char *amname)

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

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

◆ ATPrepDropColumn()

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

Definition at line 9391 of file tablecmds.c.

9394{
9395 if (rel->rd_rel->reloftype && !recursing)
9396 ereport(ERROR,
9398 errmsg("cannot drop column from typed table")));
9399
9400 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9401 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9402
9403 if (recurse)
9404 cmd->recurse = true;
9405}

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

8892{
8893 /*
8894 * Reject ONLY if there are child tables. We could implement this, but it
8895 * is a bit complicated. GENERATED clauses must be attached to the column
8896 * definition and cannot be added later like DEFAULT, so if a child table
8897 * has a generation expression that the parent does not have, the child
8898 * column will necessarily be an attislocal column. So to implement ONLY
8899 * here, we'd need extra code to update attislocal of the direct child
8900 * tables, somewhat similar to how DROP COLUMN does it, so that the
8901 * resulting state can be properly dumped and restored.
8902 */
8903 if (!recurse &&
8905 ereport(ERROR,
8907 errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
8908
8909 /*
8910 * Cannot drop generation expression from inherited columns.
8911 */
8912 if (!recursing)
8913 {
8914 HeapTuple tuple;
8916
8918 if (!HeapTupleIsValid(tuple))
8919 ereport(ERROR,
8921 errmsg("column \"%s\" of relation \"%s\" does not exist",
8922 cmd->name, RelationGetRelationName(rel))));
8923
8925
8926 if (attTup->attinhcount > 0)
8927 ereport(ERROR,
8929 errmsg("cannot drop generation expression from inherited column")));
8930 }
8931}

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

17198{
17199 Oid amoid;
17200
17201 /*
17202 * Look up the access method name and check that it differs from the
17203 * table's current AM. If DEFAULT was specified for a partitioned table
17204 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
17205 */
17206 if (amname != NULL)
17207 amoid = get_table_am_oid(amname, false);
17208 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17209 amoid = InvalidOid;
17210 else
17212
17213 /* if it's a match, phase 3 doesn't need to do anything */
17214 if (rel->rd_rel->relam == amoid)
17215 return;
17216
17217 /* Save info for Phase 3 to do the real work */
17219 tab->newAccessMethod = amoid;
17220 tab->chgAccessMethod = true;
17221}
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 17321 of file tablecmds.c.

17322{
17324
17325 /* Check that the tablespace exists */
17326 tablespaceId = get_tablespace_oid(tablespacename, false);
17327
17328 /* Check permissions except when moving to database's default */
17330 {
17332
17334 if (aclresult != ACLCHECK_OK)
17335 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
17336 }
17337
17338 /* Save info for Phase 3 to do the real work */
17339 if (OidIsValid(tab->newTableSpace))
17340 ereport(ERROR,
17342 errmsg("cannot have multiple SET TABLESPACE subcommands")));
17343
17345}

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

5407{
5408 ListCell *ltab;
5409
5410 /*
5411 * We process all the tables "in parallel", one pass at a time. This is
5412 * needed because we may have to propagate work from one table to another
5413 * (specifically, ALTER TYPE on a foreign key's PK has to dispatch the
5414 * re-adding of the foreign key constraint to the other table). Work can
5415 * only be propagated into later passes, however.
5416 */
5417 for (AlterTablePass pass = 0; pass < AT_NUM_PASSES; pass++)
5418 {
5419 /* Go through each table that needs to be processed */
5420 foreach(ltab, *wqueue)
5421 {
5423 List *subcmds = tab->subcmds[pass];
5424 ListCell *lcmd;
5425
5426 if (subcmds == NIL)
5427 continue;
5428
5429 /*
5430 * Open the relation and store it in tab. This allows subroutines
5431 * close and reopen, if necessary. Appropriate lock was obtained
5432 * by phase 1, needn't get it again.
5433 */
5434 tab->rel = relation_open(tab->relid, NoLock);
5435
5436 foreach(lcmd, subcmds)
5437 ATExecCmd(wqueue, tab,
5439 lockmode, pass, context);
5440
5441 /*
5442 * After the ALTER TYPE or SET EXPRESSION pass, do cleanup work
5443 * (this is not done in ATExecAlterColumnType since it should be
5444 * done only once if multiple columns of a table are altered).
5445 */
5447 ATPostAlterTypeCleanup(wqueue, tab, lockmode);
5448
5449 if (tab->rel)
5450 {
5451 relation_close(tab->rel, NoLock);
5452 tab->rel = NULL;
5453 }
5454 }
5455 }
5456
5457 /* Check to see if a toast table must be added. */
5458 foreach(ltab, *wqueue)
5459 {
5461
5462 /*
5463 * If the table is source table of ATTACH PARTITION command, we did
5464 * not modify anything about it that will change its toasting
5465 * requirement, so no need to check.
5466 */
5467 if (((tab->relkind == RELKIND_RELATION ||
5469 tab->partition_constraint == NULL) ||
5470 tab->relkind == RELKIND_MATVIEW)
5471 AlterTableCreateToastTable(tab->relid, (Datum) 0, lockmode);
5472 }
5473}
Expr * partition_constraint
Definition tablecmds.c:202
#define AT_NUM_PASSES
Definition tablecmds.c:170
static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
static void ATExecCmd(List **wqueue, AlteredTableInfo *tab, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTablePass cur_pass, AlterTableUtilityContext *context)
Definition tablecmds.c:5479
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 6246 of file tablecmds.c.

6247{
6252 bool needscan = false;
6255 int i;
6256 ListCell *l;
6257 EState *estate;
6258 CommandId mycid;
6259 BulkInsertState bistate;
6260 uint32 ti_options;
6261 ExprState *partqualstate = NULL;
6262
6263 /*
6264 * Open the relation(s). We have surely already locked the existing
6265 * table.
6266 */
6267 oldrel = table_open(tab->relid, NoLock);
6268 oldTupDesc = tab->oldDesc;
6269 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6270
6272 {
6274 false));
6276 }
6277 else
6278 newrel = NULL;
6279
6280 /*
6281 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6282 * is empty, so don't bother using it.
6283 */
6284 if (newrel)
6285 {
6286 mycid = GetCurrentCommandId(true);
6287 bistate = GetBulkInsertState();
6288 ti_options = TABLE_INSERT_SKIP_FSM;
6289 }
6290 else
6291 {
6292 /* keep compiler quiet about using these uninitialized */
6293 mycid = 0;
6294 bistate = NULL;
6295 ti_options = 0;
6296 }
6297
6298 /*
6299 * Generate the constraint and default execution states
6300 */
6301
6302 estate = CreateExecutorState();
6303
6304 /* Build the needed expression execution states */
6305 foreach(l, tab->constraints)
6306 {
6307 NewConstraint *con = lfirst(l);
6308
6309 switch (con->contype)
6310 {
6311 case CONSTR_CHECK:
6312 needscan = true;
6314 break;
6315 case CONSTR_FOREIGN:
6316 /* Nothing to do here */
6317 break;
6318 default:
6319 elog(ERROR, "unrecognized constraint type: %d",
6320 (int) con->contype);
6321 }
6322 }
6323
6324 /* Build expression execution states for partition check quals */
6325 if (tab->partition_constraint)
6326 {
6327 needscan = true;
6328 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6329 }
6330
6331 foreach(l, tab->newvals)
6332 {
6333 NewColumnValue *ex = lfirst(l);
6334
6335 /* expr already planned */
6336 ex->exprstate = ExecInitExpr(ex->expr, NULL);
6337 }
6338
6340 if (newrel || tab->verify_new_notnull)
6341 {
6342 /*
6343 * If we are rebuilding the tuples OR if we added any new but not
6344 * verified not-null constraints, check all *valid* not-null
6345 * constraints. This is a bit of overkill but it minimizes risk of
6346 * bugs.
6347 *
6348 * notnull_attrs does *not* collect attribute numbers for valid
6349 * not-null constraints over virtual generated columns; instead, they
6350 * are collected in notnull_virtual_attrs for verification elsewhere.
6351 */
6352 for (i = 0; i < newTupDesc->natts; i++)
6353 {
6355
6356 if (attr->attnullability == ATTNULLABLE_VALID &&
6357 !attr->attisdropped)
6358 {
6360
6361 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6363 else
6365 wholeatt->attnum);
6366 }
6367 }
6369 needscan = true;
6370 }
6371
6372 if (newrel || needscan)
6373 {
6374 ExprContext *econtext;
6377 TableScanDesc scan;
6380 ListCell *lc;
6381 Snapshot snapshot;
6383
6384 /*
6385 * When adding or changing a virtual generated column with a not-null
6386 * constraint, we need to evaluate whether the generation expression
6387 * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6388 * prepare a dummy ResultRelInfo.
6389 */
6391 {
6392 MemoryContext oldcontext;
6393
6394 Assert(newTupDesc->constr->has_generated_virtual);
6395 Assert(newTupDesc->constr->has_not_null);
6396 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6399 oldrel,
6400 0, /* dummy rangetable index */
6401 NULL,
6402 estate->es_instrument);
6403 MemoryContextSwitchTo(oldcontext);
6404 }
6405
6406 if (newrel)
6408 (errmsg_internal("rewriting table \"%s\"",
6410 else
6412 (errmsg_internal("verifying table \"%s\"",
6414
6415 if (newrel)
6416 {
6417 /*
6418 * All predicate locks on the tuples or pages are about to be made
6419 * invalid, because we move tuples around. Promote them to
6420 * relation locks.
6421 */
6423 }
6424
6425 econtext = GetPerTupleExprContext(estate);
6426
6427 /*
6428 * Create necessary tuple slots. When rewriting, two slots are needed,
6429 * otherwise one suffices. In the case where one slot suffices, we
6430 * need to use the new tuple descriptor, otherwise some constraints
6431 * can't be evaluated. Note that even when the tuple layout is the
6432 * same and no rewrite is required, the tupDescs might not be
6433 * (consider ADD COLUMN without a default).
6434 */
6435 if (tab->rewrite)
6436 {
6437 Assert(newrel != NULL);
6442
6443 /*
6444 * Set all columns in the new slot to NULL initially, to ensure
6445 * columns added as part of the rewrite are initialized to NULL.
6446 * That is necessary as tab->newvals will not contain an
6447 * expression for columns with a NULL default, e.g. when adding a
6448 * column without a default together with a column with a default
6449 * requiring an actual rewrite.
6450 */
6452 }
6453 else
6454 {
6457 newslot = NULL;
6458 }
6459
6460 /*
6461 * Any attributes that are dropped according to the new tuple
6462 * descriptor can be set to NULL. We precompute the list of dropped
6463 * attributes to avoid needing to do so in the per-tuple loop.
6464 */
6465 for (i = 0; i < newTupDesc->natts; i++)
6466 {
6467 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6469 }
6470
6471 /*
6472 * Scan through the rows, generating a new row if needed and then
6473 * checking all the constraints.
6474 */
6475 snapshot = RegisterSnapshot(GetLatestSnapshot());
6476 scan = table_beginscan(oldrel, snapshot, 0, NULL,
6477 SO_NONE);
6478
6479 /*
6480 * Switch to per-tuple memory context and reset it for each tuple
6481 * produced, so we don't leak memory.
6482 */
6484
6486 {
6488
6489 if (tab->rewrite > 0)
6490 {
6491 /* Extract data from old tuple */
6494
6495 /* copy attributes */
6496 memcpy(newslot->tts_values, oldslot->tts_values,
6497 sizeof(Datum) * oldslot->tts_nvalid);
6498 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6499 sizeof(bool) * oldslot->tts_nvalid);
6500
6501 /* Set dropped attributes to null in new tuple */
6502 foreach(lc, dropped_attrs)
6503 newslot->tts_isnull[lfirst_int(lc)] = true;
6504
6505 /*
6506 * Constraints and GENERATED expressions might reference the
6507 * tableoid column, so fill tts_tableOid with the desired
6508 * value. (We must do this each time, because it gets
6509 * overwritten with newrel's OID during storing.)
6510 */
6511 newslot->tts_tableOid = RelationGetRelid(oldrel);
6512
6513 /*
6514 * Process supplied expressions to replace selected columns.
6515 *
6516 * First, evaluate expressions whose inputs come from the old
6517 * tuple.
6518 */
6519 econtext->ecxt_scantuple = oldslot;
6520
6521 foreach(l, tab->newvals)
6522 {
6523 NewColumnValue *ex = lfirst(l);
6524
6525 if (ex->is_generated)
6526 continue;
6527
6528 newslot->tts_values[ex->attnum - 1]
6529 = ExecEvalExpr(ex->exprstate,
6530 econtext,
6531 &newslot->tts_isnull[ex->attnum - 1]);
6532 }
6533
6535
6536 /*
6537 * Now, evaluate any expressions whose inputs come from the
6538 * new tuple. We assume these columns won't reference each
6539 * other, so that there's no ordering dependency.
6540 */
6541 econtext->ecxt_scantuple = newslot;
6542
6543 foreach(l, tab->newvals)
6544 {
6545 NewColumnValue *ex = lfirst(l);
6546
6547 if (!ex->is_generated)
6548 continue;
6549
6550 newslot->tts_values[ex->attnum - 1]
6551 = ExecEvalExpr(ex->exprstate,
6552 econtext,
6553 &newslot->tts_isnull[ex->attnum - 1]);
6554 }
6555
6557 }
6558 else
6559 {
6560 /*
6561 * If there's no rewrite, old and new table are guaranteed to
6562 * have the same AM, so we can just use the old slot to verify
6563 * new constraints etc.
6564 */
6566 }
6567
6568 /* Now check any constraints on the possibly-changed tuple */
6569 econtext->ecxt_scantuple = insertslot;
6570
6572 {
6574 {
6576
6577 ereport(ERROR,
6579 errmsg("column \"%s\" of relation \"%s\" contains null values",
6580 NameStr(attr->attname),
6583 }
6584 }
6585
6587 {
6589
6591 estate,
6594 {
6596
6597 ereport(ERROR,
6599 errmsg("column \"%s\" of relation \"%s\" contains null values",
6600 NameStr(attr->attname),
6603 }
6604 }
6605
6606 foreach(l, tab->constraints)
6607 {
6608 NewConstraint *con = lfirst(l);
6609
6610 switch (con->contype)
6611 {
6612 case CONSTR_CHECK:
6613 if (!ExecCheck(con->qualstate, econtext))
6614 ereport(ERROR,
6616 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6617 con->name,
6620 break;
6621 case CONSTR_NOTNULL:
6622 case CONSTR_FOREIGN:
6623 /* Nothing to do here */
6624 break;
6625 default:
6626 elog(ERROR, "unrecognized constraint type: %d",
6627 (int) con->contype);
6628 }
6629 }
6630
6631 if (partqualstate && !ExecCheck(partqualstate, econtext))
6632 {
6633 if (tab->validate_default)
6634 ereport(ERROR,
6636 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6638 errtable(oldrel)));
6639 else
6640 ereport(ERROR,
6642 errmsg("partition constraint of relation \"%s\" is violated by some row",
6644 errtable(oldrel)));
6645 }
6646
6647 /* Write the tuple out to the new relation */
6648 if (newrel)
6650 ti_options, bistate);
6651
6652 ResetExprContext(econtext);
6653
6655 }
6656
6658 table_endscan(scan);
6659 UnregisterSnapshot(snapshot);
6660
6662 if (newslot)
6664 }
6665
6666 FreeExecutorState(estate);
6667
6669 if (newrel)
6670 {
6671 FreeBulkInsertState(bistate);
6672
6673 table_finish_bulk_insert(newrel, ti_options);
6674
6676 }
6677}
uint32_t uint32
Definition c.h:683
uint32 CommandId
Definition c.h:809
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
int int errmsg_internal(const char *fmt,...) pg_attribute_printf(1
#define DEBUG1
Definition elog.h:31
ExprState * ExecInitExpr(Expr *node, PlanState *parent)
Definition execExpr.c:143
bool ExecCheck(ExprState *state, ExprContext *econtext)
Definition execExpr.c:872
void InitResultRelInfo(ResultRelInfo *resultRelInfo, Relation resultRelationDesc, Index resultRelationIndex, ResultRelInfo *partition_root_rri, int instrument_options)
Definition execMain.c:1308
AttrNumber ExecRelGenVirtualNotNull(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, List *notnull_virtual_attrs)
Definition execMain.c:2160
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
#define ResetExprContext(econtext)
Definition executor.h:659
#define GetPerTupleMemoryContext(estate)
Definition executor.h:670
BulkInsertState GetBulkInsertState(void)
Definition heapam.c:1938
void FreeBulkInsertState(BulkInsertState bistate)
Definition heapam.c:1955
List * lappend_int(List *list, int datum)
Definition list.c:357
bool CheckRelationOidLockedByMe(Oid relid, LOCKMODE lockmode, bool orstronger)
Definition lmgr.c:351
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
#define foreach_int(var, lst)
Definition pg_list.h:502
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition predicate.c:3052
int errtablecol(Relation rel, int attnum)
Definition relcache.c:6101
Snapshot GetLatestSnapshot(void)
Definition snapmgr.c:354
void UnregisterSnapshot(Snapshot snapshot)
Definition snapmgr.c:866
Snapshot RegisterSnapshot(Snapshot snapshot)
Definition snapmgr.c:824
bool attisdropped
Definition tupdesc.h:78
char attnullability
Definition tupdesc.h:80
int es_instrument
Definition execnodes.h:757
MemoryContext es_query_cxt
Definition execnodes.h:747
TupleTableSlot * ecxt_scantuple
Definition execnodes.h:287
ConstrType contype
Definition tablecmds.c:221
ExprState * qualstate
Definition tablecmds.c:227
const TupleTableSlotOps * table_slot_callbacks(Relation relation)
Definition tableam.c:59
@ SO_NONE
Definition tableam.h:49
#define TABLE_INSERT_SKIP_FSM
Definition tableam.h:284
static void table_finish_bulk_insert(Relation rel, uint32 options)
Definition tableam.h:1661
static void table_tuple_insert(Relation rel, TupleTableSlot *slot, CommandId cid, uint32 options, BulkInsertStateData *bistate)
Definition tableam.h:1458
static TableScanDesc table_beginscan(Relation rel, Snapshot snapshot, int nkeys, ScanKeyData *key, uint32 flags)
Definition tableam.h:943
static bool table_scan_getnextslot(TableScanDesc sscan, ScanDirection direction, TupleTableSlot *slot)
Definition tableam.h:1096
#define ATTNULLABLE_VALID
Definition tupdesc.h:86
static CompactAttribute * TupleDescCompactAttr(TupleDesc tupdesc, int i)
Definition tupdesc.h:195
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition tuptable.h:476
static void slot_getallattrs(TupleTableSlot *slot)
Definition tuptable.h:390
static bool slot_attisnull(TupleTableSlot *slot, int attnum)
Definition tuptable.h:403
CommandId GetCurrentCommandId(bool used)
Definition xact.c:831

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

Referenced by ATRewriteTables().

◆ ATRewriteTables()

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

Definition at line 5957 of file tablecmds.c.

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

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

6865{
6866 int actual_target;
6867
6868 switch (rel->rd_rel->relkind)
6869 {
6870 case RELKIND_RELATION:
6872 break;
6875 break;
6876 case RELKIND_VIEW:
6878 break;
6879 case RELKIND_MATVIEW:
6881 break;
6882 case RELKIND_INDEX:
6884 break;
6887 break;
6890 break;
6893 break;
6894 case RELKIND_SEQUENCE:
6896 break;
6897 default:
6898 actual_target = 0;
6899 break;
6900 }
6901
6902 /* Wrong target type? */
6903 if ((actual_target & allowed_targets) == 0)
6904 {
6905 const char *action_str = alter_table_type_to_string(cmdtype);
6906
6907 if (action_str)
6908 ereport(ERROR,
6910 /* translator: %s is a group of some SQL keywords */
6911 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6913 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6914 else
6915 /* internal error? */
6916 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6918 }
6919
6920 /* Permissions checks */
6924
6925 /*
6926 * Conflict log tables are used internally for logical replication
6927 * conflict logging and should not be altered directly, as it could
6928 * disrupt conflict logging. Direct ALTER commands are already rejected
6929 * during relation lookup in RangeVarCallbackForAlterRelation(), and
6930 * AlterTableMoveAll() skips these tables, so a conflict log table does
6931 * not normally reach here; this check guards any internal caller that
6932 * arrives via AlterTableInternal().
6933 */
6935 ereport(ERROR,
6937 errmsg("cannot alter conflict log table \"%s\"",
6939 errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
6940
6942 ereport(ERROR,
6944 errmsg("permission denied: \"%s\" is a system catalog",
6946}
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition tablecmds.c:6717

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(), errdetail_relkind_not_supported(), errmsg, ERROR, fb(), get_relkind_objtype(), GetUserId(), IsConflictLogTableClass(), 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 6957 of file tablecmds.c.

6960{
6961 /*
6962 * Propagate to children, if desired and if there are (or might be) any
6963 * children.
6964 */
6965 if (recurse && rel->rd_rel->relhassubclass)
6966 {
6967 Oid relid = RelationGetRelid(rel);
6968 ListCell *child;
6969 List *children;
6970
6971 children = find_all_inheritors(relid, lockmode, NULL);
6972
6973 /*
6974 * find_all_inheritors does the recursive search of the inheritance
6975 * hierarchy, so all we have to do is process all of the relids in the
6976 * list that it returns.
6977 */
6978 foreach(child, children)
6979 {
6980 Oid childrelid = lfirst_oid(child);
6982
6983 if (childrelid == relid)
6984 continue;
6985 /* find_all_inheritors already got lock */
6988 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
6990 }
6991 }
6992}

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

21360{
21361 List *idxes;
21365 ListCell *cell;
21366 MemoryContext cxt;
21368
21370 "AttachPartitionEnsureIndexes",
21373
21378
21379 /* Build arrays of all existing indexes and their IndexInfos */
21381 {
21383
21386 }
21387
21388 /*
21389 * If we're attaching a foreign table, we must fail if any of the indexes
21390 * is a constraint index; otherwise, there's nothing to do here. Do this
21391 * before starting work, to avoid wasting the effort of building a few
21392 * non-unique indexes before coming across a unique one.
21393 */
21394 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
21395 {
21396 foreach(cell, idxes)
21397 {
21398 Oid idx = lfirst_oid(cell);
21400
21401 if (idxRel->rd_index->indisunique ||
21402 idxRel->rd_index->indisprimary)
21403 ereport(ERROR,
21405 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
21408 errdetail("Partitioned table \"%s\" contains unique indexes.",
21411 }
21412
21413 goto out;
21414 }
21415
21416 /*
21417 * For each index on the partitioned table, find a matching one in the
21418 * partition-to-be; if one is not found, create one.
21419 */
21420 foreach(cell, idxes)
21421 {
21422 Oid idx = lfirst_oid(cell);
21424 IndexInfo *info;
21425 AttrMap *attmap;
21426 bool found = false;
21428
21429 /*
21430 * Ignore indexes in the partitioned table other than partitioned
21431 * indexes.
21432 */
21433 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
21434 {
21436 continue;
21437 }
21438
21439 /* construct an indexinfo to compare existing indexes against */
21440 info = BuildIndexInfo(idxRel);
21442 RelationGetDescr(rel),
21443 false);
21445
21446 /*
21447 * Scan the list of existing indexes in the partition-to-be, and mark
21448 * the first matching, valid, unattached one we find, if any, as
21449 * partition of the parent index. If we find one, we're done.
21450 */
21451 for (int i = 0; i < list_length(attachRelIdxs); i++)
21452 {
21455
21456 /* does this index have a parent? if so, can't use it */
21457 if (attachrelIdxRels[i]->rd_rel->relispartition)
21458 continue;
21459
21460 /* If this index is invalid, can't use it */
21461 if (!attachrelIdxRels[i]->rd_index->indisvalid)
21462 continue;
21463
21464 if (CompareIndexInfo(attachInfos[i], info,
21465 attachrelIdxRels[i]->rd_indcollation,
21466 idxRel->rd_indcollation,
21467 attachrelIdxRels[i]->rd_opfamily,
21468 idxRel->rd_opfamily,
21469 attmap))
21470 {
21471 /*
21472 * If this index is being created in the parent because of a
21473 * constraint, then the child needs to have a constraint also,
21474 * so look for one. If there is no such constraint, this
21475 * index is no good, so keep looking.
21476 */
21478 {
21479 cldConstrOid =
21481 cldIdxId);
21482 /* no dice */
21484 continue;
21485
21486 /* Ensure they're both the same type of constraint */
21489 continue;
21490 }
21491
21492 /* bingo. */
21497 found = true;
21498
21500 break;
21501 }
21502 }
21503
21504 /*
21505 * If no suitable index was found in the partition-to-be, create one
21506 * now. Note that if this is a PK, not-null constraints must already
21507 * exist.
21508 */
21509 if (!found)
21510 {
21511 IndexStmt *stmt;
21512 Oid conOid;
21513
21515 idxRel, attmap,
21516 &conOid);
21520 conOid,
21521 -1,
21522 true, false, false, false, false);
21523 }
21524
21526 }
21527
21528out:
21529 /* Clean up. */
21530 for (int i = 0; i < list_length(attachRelIdxs); i++)
21534}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:263
char get_constraint_type(Oid conoid)
Definition lsyscache.c:1369
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
#define foreach_current_index(var_or_cell)
Definition pg_list.h:435

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

Referenced by attachPartitionTable().

◆ AttachPartitionForeignKey()

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

Definition at line 11938 of file tablecmds.c.

11945{
11950 bool queueValidation;
11954
11955 /* Fetch the parent constraint tuple */
11959 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11961 parentConstrIsEnforced = parentConstr->conenforced;
11962
11963 /* Fetch the child constraint tuple */
11967 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11969 partConstrFrelid = partConstr->confrelid;
11970 partConstrRelid = partConstr->conrelid;
11971
11972 /*
11973 * If the referenced table is partitioned, then the partition we're
11974 * attaching now has extra pg_constraint rows and action triggers that are
11975 * no longer needed. Remove those.
11976 */
11978 {
11980
11983
11985 }
11986
11987 /*
11988 * Will we need to validate this constraint? A valid parent constraint
11989 * implies that all child constraints have been validated, so if this one
11990 * isn't, we must trigger phase 3 validation.
11991 */
11992 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11993
11996
11997 /*
11998 * The action triggers in the new partition become redundant -- the parent
11999 * table already has equivalent ones, and those will be able to reach the
12000 * partition. Remove the ones in the partition. We identify them because
12001 * they have our constraint OID, as well as being on the referenced rel.
12002 */
12005
12008
12009 /*
12010 * Like the constraint, attach partition's "check" triggers to the
12011 * corresponding parent triggers if the constraint is ENFORCED. NOT
12012 * ENFORCED constraints do not have these triggers.
12013 */
12015 {
12018
12028 }
12029
12030 /*
12031 * We updated this pg_constraint row above to set its parent; validating
12032 * it will cause its convalidated flag to change, so we need CCI here. In
12033 * addition, we need it unconditionally for the rare case where the parent
12034 * table has *two* identical constraints; when reaching this function for
12035 * the second one, we must have made our changes visible, otherwise we
12036 * would try to attach both to this one.
12037 */
12039
12040 /* If validation is needed, put it in the queue now. */
12041 if (queueValidation)
12042 {
12044 Oid confrelid;
12045
12047
12050 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
12051
12052 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
12053
12054 /* Use the same lock as for AT_ValidateConstraint */
12059 }
12060}
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:1234

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

20987{
20988 /*
20989 * Create an inheritance; the relevant checks are performed inside the
20990 * function.
20991 */
20992 CreateInheritance(attachrel, rel, true);
20993
20994 /* Update the pg_class entry. */
20995 StorePartitionBound(attachrel, rel, bound);
20996
20997 /* Ensure there exists a correct set of indexes in the partition. */
20999
21000 /* and triggers */
21002
21003 /*
21004 * Clone foreign key constraints. Callee is responsible for setting up
21005 * for phase 3 constraint verification.
21006 */
21008}
void StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound)
Definition heap.c:4074
static void CloneRowTriggersToPartition(Relation parent, Relation partition)
static void AttachPartitionEnsureIndexes(List **wqueue, Relation rel, Relation attachrel)
static void CloneForeignKeyConstraints(List **wqueue, Relation parentRel, Relation partitionRel)

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

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

◆ ATTypedTableRecursion()

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

Definition at line 7032 of file tablecmds.c.

7034{
7035 ListCell *child;
7036 List *children;
7037
7038 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7039
7040 children = find_typed_table_dependencies(rel->rd_rel->reltype,
7042 cmd->behavior);
7043
7044 foreach(child, children)
7045 {
7046 Oid childrelid = lfirst_oid(child);
7048
7049 childrel = relation_open(childrelid, lockmode);
7051 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
7053 }
7054}
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition tablecmds.c:7235

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

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

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

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

◆ buildExpressionExecutionStates()

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

Definition at line 22868 of file tablecmds.c.

22869{
22870 /*
22871 * Build the needed expression execution states. Here, we expect only NOT
22872 * NULL and CHECK constraint.
22873 */
22875 {
22876 switch (con->contype)
22877 {
22878 case CONSTR_CHECK:
22879
22880 /*
22881 * We already expanded virtual expression in
22882 * createTableConstraints.
22883 */
22884 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22885 break;
22886 case CONSTR_NOTNULL:
22887 /* Nothing to do here. */
22888 break;
22889 default:
22890 elog(ERROR, "unrecognized constraint type: %d",
22891 (int) con->contype);
22892 }
22893 }
22894
22895 /* Expression already planned in createTableConstraints */
22897 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22898}

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

17020{
17022 SysScanDesc scan;
17023 ScanKeyData key[1];
17025
17027 ScanKeyInit(&key[0],
17030 ObjectIdGetDatum(relationOid));
17032 true, NULL, 1, key);
17034 {
17039 Acl *newAcl;
17041 bool isNull;
17042 HeapTuple newtuple;
17043
17044 /* Ignore dropped columns */
17045 if (att->attisdropped)
17046 continue;
17047
17051 &isNull);
17052 /* Null ACLs do not require changes */
17053 if (isNull)
17054 continue;
17055
17056 memset(repl_null, false, sizeof(repl_null));
17057 memset(repl_repl, false, sizeof(repl_repl));
17058
17063
17067
17068 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
17069
17070 heap_freetuple(newtuple);
17071 }
17072 systable_endscan(scan);
17074}

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

17085{
17087 SysScanDesc scan;
17088 ScanKeyData key[2];
17089 HeapTuple tup;
17090
17091 /*
17092 * SERIAL sequences are those having an auto dependency on one of the
17093 * table's columns (we don't care *which* column, exactly).
17094 */
17096
17097 ScanKeyInit(&key[0],
17101 ScanKeyInit(&key[1],
17104 ObjectIdGetDatum(relationOid));
17105 /* we leave refobjsubid unspecified */
17106
17108 NULL, 2, key);
17109
17110 while (HeapTupleIsValid(tup = systable_getnext(scan)))
17111 {
17114
17115 /* skip dependencies other than auto dependencies on columns */
17116 if (depForm->refobjsubid == 0 ||
17117 depForm->classid != RelationRelationId ||
17118 depForm->objsubid != 0 ||
17119 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
17120 continue;
17121
17122 /* Use relation_open just in case it's an index */
17123 seqRel = relation_open(depForm->objid, lockmode);
17124
17125 /* skip non-sequence relations */
17126 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
17127 {
17128 /* No need to keep the lock */
17129 relation_close(seqRel, lockmode);
17130 continue;
17131 }
17132
17133 /* We don't need to close the sequence while we alter it. */
17134 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
17135
17136 /* Now we can close it. Keep the lock till end of transaction. */
17138 }
17139
17140 systable_endscan(scan);
17141
17143}

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

7789{
7791 int attnum;
7792
7793 /*
7794 * this test is deliberately not attisdropped-aware, since if one tries to
7795 * add a column matching a dropped column name, it's gonna fail anyway.
7796 */
7799 PointerGetDatum(colname));
7801 return true;
7802
7805
7806 /*
7807 * We throw a different error message for conflicts with system column
7808 * names, since they are normally not shown and the user might otherwise
7809 * be confused about the reason for the conflict.
7810 */
7811 if (attnum <= 0)
7812 ereport(ERROR,
7814 errmsg("column name \"%s\" conflicts with a system column name",
7815 colname)));
7816 else
7817 {
7818 if (if_not_exists)
7819 {
7822 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7823 colname, RelationGetRelationName(rel))));
7824 return false;
7825 }
7826
7827 ereport(ERROR,
7829 errmsg("column \"%s\" of relation \"%s\" already exists",
7830 colname, RelationGetRelationName(rel))));
7831 }
7832
7833 return true;
7834}
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 7284 of file tablecmds.c.

7285{
7287 bool typeOk = false;
7288
7289 if (typ->typtype == TYPTYPE_COMPOSITE)
7290 {
7292
7293 Assert(OidIsValid(typ->typrelid));
7295 typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
7296
7297 /*
7298 * Close the parent rel, but keep our AccessShareLock on it until xact
7299 * commit. That will prevent someone else from deleting or ALTERing
7300 * the type before the typed table creation/conversion commits.
7301 */
7303
7304 if (!typeOk)
7305 ereport(ERROR,
7307 errmsg("type %s is the row type of another table",
7308 format_type_be(typ->oid)),
7309 errdetail("A typed table must use a stand-alone composite type created with CREATE TYPE.")));
7310 }
7311 else
7312 ereport(ERROR,
7314 errmsg("type %s is not a composite type",
7315 format_type_be(typ->oid))));
7316}

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

4542{
4543 /*
4544 * Don't allow ALTER on temp tables of other backends. Their local buffer
4545 * manager is not going to cope if we need to change the table's contents.
4546 * Even if we don't, there may be optimizations that assume temp tables
4547 * aren't subject to such interference.
4548 */
4549 if (RELATION_IS_OTHER_TEMP(rel))
4550 ereport(ERROR,
4552 errmsg("cannot alter temporary tables of other sessions")));
4553
4554 /*
4555 * Also check for active uses of the relation in the current transaction,
4556 * including open scans and pending AFTER trigger events.
4557 */
4558 CheckTableNotInUse(rel, "ALTER TABLE");
4559}
void CheckTableNotInUse(Relation rel, const char *stmt)
Definition tablecmds.c:4508

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

14203{
14204 Oid roleid = GetUserId();
14206 int i;
14207
14208 /* Okay if we have relation-level REFERENCES permission */
14211 if (aclresult == ACLCHECK_OK)
14212 return;
14213 /* Else we must have REFERENCES on each column */
14214 for (i = 0; i < natts; i++)
14215 {
14217 roleid, ACL_REFERENCES);
14218 if (aclresult != ACLCHECK_OK)
14221 }
14222}
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition aclchk.c:3934
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4105
#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 3772 of file tablecmds.c.

3773{
3775
3776 /*
3777 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3778 * stored as 0.
3779 */
3780 oldTableSpaceId = rel->rd_rel->reltablespace;
3783 return false;
3784
3785 /*
3786 * We cannot support moving mapped relations into different tablespaces.
3787 * (In particular this eliminates all shared catalogs.)
3788 */
3789 if (RelationIsMapped(rel))
3790 ereport(ERROR,
3792 errmsg("cannot move system relation \"%s\"",
3794
3795 /* Cannot move a non-shared relation into pg_global */
3797 ereport(ERROR,
3799 errmsg("only shared relations can be placed in pg_global tablespace")));
3800
3801 /*
3802 * Do not allow moving temp tables of other backends ... their local
3803 * buffer manager is not going to cope.
3804 */
3805 if (RELATION_IS_OTHER_TEMP(rel))
3806 ereport(ERROR,
3808 errmsg("cannot move temporary tables of other sessions")));
3809
3810 return true;
3811}
#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 4508 of file tablecmds.c.

4509{
4510 int expected_refcnt;
4511
4512 expected_refcnt = rel->rd_isnailed ? 2 : 1;
4513 if (rel->rd_refcnt != expected_refcnt)
4514 ereport(ERROR,
4516 /* translator: first %s is a SQL command, eg ALTER TABLE */
4517 errmsg("cannot %s \"%s\" because it is being used by active queries in this session",
4519
4520 if (rel->rd_rel->relkind != RELKIND_INDEX &&
4521 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
4523 ereport(ERROR,
4525 /* translator: first %s is a SQL command, eg ALTER TABLE */
4526 errmsg("cannot %s \"%s\" because it has pending trigger events",
4528}
int rd_refcnt
Definition rel.h:59
bool rd_isnailed
Definition rel.h:62
bool AfterTriggerPendingOnRel(Oid relid)
Definition trigger.c:6175

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

10007{
10008 char buf[NAMEDATALEN * 2];
10009 int buflen = 0;
10010 ListCell *lc;
10011
10012 buf[0] = '\0';
10013 foreach(lc, colnames)
10014 {
10015 const char *name = strVal(lfirst(lc));
10016
10017 if (buflen > 0)
10018 buf[buflen++] = '_'; /* insert _ between names */
10019
10020 /*
10021 * At this point we have buflen <= NAMEDATALEN. name should be less
10022 * than NAMEDATALEN already, but use strlcpy for paranoia.
10023 */
10024 strlcpy(buf + buflen, name, NAMEDATALEN);
10025 buflen += strlen(buf + buflen);
10026 if (buflen >= NAMEDATALEN)
10027 break;
10028 }
10029 return pstrdup(buf);
10030}
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 11394 of file tablecmds.c.

11395{
11397 AttrMap *attmap;
11398 ListCell *cell;
11399 SysScanDesc scan;
11400 ScanKeyData key[2];
11401 HeapTuple tuple;
11402 List *clone = NIL;
11404
11405 /*
11406 * Search for any constraints where this partition's parent is in the
11407 * referenced side. However, we must not clone any constraint whose
11408 * parent constraint is also going to be cloned, to avoid duplicates. So
11409 * do it in two steps: first construct the list of constraints to clone,
11410 * then go over that list cloning those whose parents are not in the list.
11411 * (We must not rely on the parent being seen first, since the catalog
11412 * scan could return children first.)
11413 */
11415 ScanKeyInit(&key[0],
11418 ScanKeyInit(&key[1],
11421 /* This is a seqscan, as we don't have a usable index ... */
11423 NULL, 2, key);
11424 while ((tuple = systable_getnext(scan)) != NULL)
11425 {
11427
11429 }
11430 systable_endscan(scan);
11432
11433 /*
11434 * Triggers of the foreign keys will be manipulated a bunch of times in
11435 * the loop below. To avoid repeatedly opening/closing the trigger
11436 * catalog relation, we open it here and pass it to the subroutines called
11437 * below.
11438 */
11440
11443 false);
11444 foreach(cell, clone)
11445 {
11446 Oid constrOid = lfirst_oid(cell);
11449 Oid indexOid;
11451 int numfks;
11458 int numfkdelsetcols;
11459 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11461 ObjectAddress address;
11464
11466 if (!HeapTupleIsValid(tuple))
11467 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
11469
11470 /*
11471 * As explained above: don't try to clone a constraint for which we're
11472 * going to clone the parent.
11473 */
11474 if (list_member_oid(clone, constrForm->conparentid))
11475 {
11476 ReleaseSysCache(tuple);
11477 continue;
11478 }
11479
11480 /* We need the same lock level that CreateTrigger will acquire */
11482
11483 indexOid = constrForm->conindid;
11485 &numfks,
11486 conkey,
11487 confkey,
11488 conpfeqop,
11489 conppeqop,
11490 conffeqop,
11492 confdelsetcols);
11493
11494 for (int i = 0; i < numfks; i++)
11495 mapped_confkey[i] = attmap->attnums[confkey[i] - 1];
11496
11499 fkconstraint->conname = NameStr(constrForm->conname);
11500 fkconstraint->deferrable = constrForm->condeferrable;
11501 fkconstraint->initdeferred = constrForm->condeferred;
11502 fkconstraint->location = -1;
11503 fkconstraint->pktable = NULL;
11504 /* ->fk_attrs determined below */
11505 fkconstraint->pk_attrs = NIL;
11506 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11507 fkconstraint->fk_upd_action = constrForm->confupdtype;
11508 fkconstraint->fk_del_action = constrForm->confdeltype;
11509 fkconstraint->fk_del_set_cols = NIL;
11510 fkconstraint->old_conpfeqop = NIL;
11511 fkconstraint->old_pktable_oid = InvalidOid;
11512 fkconstraint->is_enforced = constrForm->conenforced;
11513 fkconstraint->skip_validation = false;
11514 fkconstraint->initially_valid = constrForm->convalidated;
11515
11516 /* set up colnames that are used to generate the constraint name */
11517 for (int i = 0; i < numfks; i++)
11518 {
11520
11522 conkey[i] - 1);
11523 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11524 makeString(NameStr(att->attname)));
11525 }
11526
11527 /*
11528 * Add the new foreign key constraint pointing to the new partition.
11529 * Because this new partition appears in the referenced side of the
11530 * constraint, we don't need to set up for Phase 3 check.
11531 */
11533 if (!OidIsValid(partIndexId))
11534 elog(ERROR, "index for %u not found in partition %s",
11536
11537 /*
11538 * Get the "action" triggers belonging to the constraint to pass as
11539 * parent OIDs for similar triggers that will be created on the
11540 * partition in addFkRecurseReferenced().
11541 */
11542 if (constrForm->conenforced)
11544 constrForm->confrelid, constrForm->conrelid,
11546
11547 /* Add this constraint ... */
11549 fkconstraint->conname, fkconstraint, fkRel,
11553 numfkdelsetcols, confdelsetcols, false,
11554 constrForm->conperiod);
11555 /* ... and recurse */
11557 fkRel,
11560 address.objectId,
11561 numfks,
11563 conkey,
11564 conpfeqop,
11565 conppeqop,
11566 conffeqop,
11568 confdelsetcols,
11569 true,
11572 constrForm->conperiod);
11573
11575 ReleaseSysCache(tuple);
11576 }
11577
11579}
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 11595 of file tablecmds.c.

11596{
11597 AttrMap *attmap;
11598 List *partFKs;
11599 List *clone = NIL;
11600 ListCell *cell;
11602
11603 /* obtain a list of constraints that we need to clone */
11604 foreach(cell, RelationGetFKeyList(parentRel))
11605 {
11606 ForeignKeyCacheInfo *fk = lfirst(cell);
11607
11608 /*
11609 * Refuse to attach a table as partition that this partitioned table
11610 * already has a foreign key to. This isn't useful schema, which is
11611 * proven by the fact that there have been no user complaints that
11612 * it's already impossible to achieve this in the opposite direction,
11613 * i.e., creating a foreign key that references a partition. This
11614 * restriction allows us to dodge some complexities around
11615 * pg_constraint and pg_trigger row creations that would be needed
11616 * during ATTACH/DETACH for this kind of relationship.
11617 */
11618 if (fk->confrelid == RelationGetRelid(partRel))
11619 ereport(ERROR,
11621 errmsg("cannot attach table \"%s\" as a partition because it is referenced by foreign key \"%s\"",
11622 RelationGetRelationName(partRel),
11623 get_constraint_name(fk->conoid))));
11624
11625 clone = lappend_oid(clone, fk->conoid);
11626 }
11627
11628 /*
11629 * Silently do nothing if there's nothing to do. In particular, this
11630 * avoids throwing a spurious error for foreign tables.
11631 */
11632 if (clone == NIL)
11633 return;
11634
11635 if (partRel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11636 ereport(ERROR,
11638 errmsg("foreign key constraints are not supported on foreign tables")));
11639
11640 /*
11641 * Triggers of the foreign keys will be manipulated a bunch of times in
11642 * the loop below. To avoid repeatedly opening/closing the trigger
11643 * catalog relation, we open it here and pass it to the subroutines called
11644 * below.
11645 */
11647
11648 /*
11649 * The constraint key may differ, if the columns in the partition are
11650 * different. This map is used to convert them.
11651 */
11654 false);
11655
11657
11658 foreach(cell, clone)
11659 {
11663 HeapTuple tuple;
11664 int numfks;
11671 int numfkdelsetcols;
11672 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
11674 bool attached;
11675 Oid indexOid;
11676 ObjectAddress address;
11677 ListCell *lc;
11680 bool with_period;
11681
11683 if (!HeapTupleIsValid(tuple))
11684 elog(ERROR, "cache lookup failed for constraint %u",
11687
11688 /* Don't clone constraints whose parents are being cloned */
11689 if (list_member_oid(clone, constrForm->conparentid))
11690 {
11691 ReleaseSysCache(tuple);
11692 continue;
11693 }
11694
11695 /*
11696 * Need to prevent concurrent deletions. If pkrel is a partitioned
11697 * relation, that means to lock all partitions.
11698 */
11700 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11703
11706 &numfkdelsetcols, confdelsetcols);
11707 for (int i = 0; i < numfks; i++)
11708 mapped_conkey[i] = attmap->attnums[conkey[i] - 1];
11709
11710 /*
11711 * Get the "check" triggers belonging to the constraint, if it is
11712 * ENFORCED, to pass as parent OIDs for similar triggers that will be
11713 * created on the partition in addFkRecurseReferencing(). They are
11714 * also passed to tryAttachPartitionForeignKey() below to simply
11715 * assign as parents to the partition's existing "check" triggers,
11716 * that is, if the corresponding constraints is deemed attachable to
11717 * the parent constraint.
11718 */
11719 if (constrForm->conenforced)
11721 constrForm->confrelid, constrForm->conrelid,
11723
11724 /*
11725 * Before creating a new constraint, see whether any existing FKs are
11726 * fit for the purpose. If one is, attach the parent constraint to
11727 * it, and don't clone anything. This way we avoid the expensive
11728 * verification step and don't end up with a duplicate FK, and we
11729 * don't need to recurse to partitions for this constraint.
11730 */
11731 attached = false;
11732 foreach(lc, partFKs)
11733 {
11735
11737 fk,
11738 partRel,
11740 numfks,
11742 confkey,
11743 conpfeqop,
11746 trigrel))
11747 {
11748 attached = true;
11750 break;
11751 }
11752 }
11753 if (attached)
11754 {
11755 ReleaseSysCache(tuple);
11756 continue;
11757 }
11758
11759 /* No dice. Set up to create our own constraint */
11762 /* ->conname determined below */
11763 fkconstraint->deferrable = constrForm->condeferrable;
11764 fkconstraint->initdeferred = constrForm->condeferred;
11765 fkconstraint->location = -1;
11766 fkconstraint->pktable = NULL;
11767 /* ->fk_attrs determined below */
11768 fkconstraint->pk_attrs = NIL;
11769 fkconstraint->fk_matchtype = constrForm->confmatchtype;
11770 fkconstraint->fk_upd_action = constrForm->confupdtype;
11771 fkconstraint->fk_del_action = constrForm->confdeltype;
11772 fkconstraint->fk_del_set_cols = NIL;
11773 fkconstraint->old_conpfeqop = NIL;
11774 fkconstraint->old_pktable_oid = InvalidOid;
11775 fkconstraint->is_enforced = constrForm->conenforced;
11776 fkconstraint->skip_validation = false;
11777 fkconstraint->initially_valid = constrForm->convalidated;
11778 for (int i = 0; i < numfks; i++)
11779 {
11781
11782 att = TupleDescAttr(RelationGetDescr(partRel),
11783 mapped_conkey[i] - 1);
11784 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
11785 makeString(NameStr(att->attname)));
11786 }
11787
11788 indexOid = constrForm->conindid;
11789 with_period = constrForm->conperiod;
11790
11791 /* Create the pg_constraint entry at this level */
11793 NameStr(constrForm->conname), fkconstraint,
11794 partRel, pkrel, indexOid, parentConstrOid,
11795 numfks, confkey,
11798 numfkdelsetcols, confdelsetcols,
11799 false, with_period);
11800
11801 /* Done with the cloned constraint's tuple */
11802 ReleaseSysCache(tuple);
11803
11804 /* Create the check triggers, and recurse to partitions, if any */
11807 partRel,
11808 pkrel,
11809 indexOid,
11810 address.objectId,
11811 numfks,
11812 confkey,
11814 conpfeqop,
11815 conppeqop,
11816 conffeqop,
11818 confdelsetcols,
11819 false, /* no old check exists */
11823 with_period);
11825 }
11826
11828}

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

11367{
11368 /* This only works for declarative partitioning */
11369 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11370
11371 /*
11372 * First, clone constraints where the parent is on the referencing side.
11373 */
11375
11376 /*
11377 * Clone constraints for which the parent is on the referenced side.
11378 */
11380}
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 21542 of file tablecmds.c.

21543{
21546 SysScanDesc scan;
21547 HeapTuple tuple;
21549
21554 true, NULL, 1, &key);
21555
21557 "clone trig", ALLOCSET_SMALL_SIZES);
21558
21559 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
21560 {
21563 Node *qual = NULL;
21564 Datum value;
21565 bool isnull;
21566 List *cols = NIL;
21567 List *trigargs = NIL;
21569
21570 /*
21571 * Ignore statement-level triggers; those are not cloned.
21572 */
21573 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
21574 continue;
21575
21576 /*
21577 * Don't clone internal triggers, because the constraint cloning code
21578 * will.
21579 */
21580 if (trigForm->tgisinternal)
21581 continue;
21582
21583 /*
21584 * Complain if we find an unexpected trigger type.
21585 */
21586 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
21587 !TRIGGER_FOR_AFTER(trigForm->tgtype))
21588 elog(ERROR, "unexpected trigger \"%s\" found",
21589 NameStr(trigForm->tgname));
21590
21591 /* Use short-lived context for CREATE TRIGGER */
21593
21594 /*
21595 * If there is a WHEN clause, generate a 'cooked' version of it that's
21596 * appropriate for the partition.
21597 */
21599 RelationGetDescr(pg_trigger), &isnull);
21600 if (!isnull)
21601 {
21603 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
21604 partition, parent);
21605 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
21606 partition, parent);
21607 }
21608
21609 /*
21610 * If there is a column list, transform it to a list of column names.
21611 * Note we don't need to map this list in any way ...
21612 */
21613 if (trigForm->tgattr.dim1 > 0)
21614 {
21615 int i;
21616
21617 for (i = 0; i < trigForm->tgattr.dim1; i++)
21618 {
21620
21621 col = TupleDescAttr(parent->rd_att,
21622 trigForm->tgattr.values[i] - 1);
21623 cols = lappend(cols,
21624 makeString(pstrdup(NameStr(col->attname))));
21625 }
21626 }
21627
21628 /* Reconstruct trigger arguments list. */
21629 if (trigForm->tgnargs > 0)
21630 {
21631 char *p;
21632
21634 RelationGetDescr(pg_trigger), &isnull);
21635 if (isnull)
21636 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
21638
21639 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
21640
21641 for (int i = 0; i < trigForm->tgnargs; i++)
21642 {
21644 p += strlen(p) + 1;
21645 }
21646 }
21647
21649 trigStmt->replace = false;
21650 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
21651 trigStmt->trigname = NameStr(trigForm->tgname);
21652 trigStmt->relation = NULL;
21653 trigStmt->funcname = NULL; /* passed separately */
21654 trigStmt->args = trigargs;
21655 trigStmt->row = true;
21656 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
21657 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
21658 trigStmt->columns = cols;
21659 trigStmt->whenClause = NULL; /* passed separately */
21660 trigStmt->transitionRels = NIL; /* not supported at present */
21661 trigStmt->deferrable = trigForm->tgdeferrable;
21662 trigStmt->initdeferred = trigForm->tginitdeferred;
21663 trigStmt->constrrel = NULL; /* passed separately */
21664
21666 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
21667 trigForm->tgfoid, trigForm->oid, qual,
21668 false, true, trigForm->tgenabled);
21669
21672 }
21673
21675
21676 systable_endscan(scan);
21678}
#define DatumGetByteaPP(X)
Definition fmgr.h:292
static struct @175 value
void MemoryContextReset(MemoryContext context)
Definition mcxt.c:406
#define ALLOCSET_SMALL_SIZES
Definition memutils.h:170
#define PRS2_OLD_VARNO
Definition primnodes.h:251
#define PRS2_NEW_VARNO
Definition primnodes.h:252
ObjectAddress CreateTriggerFiringOn(const CreateTrigStmt *stmt, const char *queryString, Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, Oid funcoid, Oid parentTriggerOid, Node *whenClause, bool isInternal, bool in_partition, char trigger_fires_when)
Definition trigger.c:179
static char * VARDATA_ANY(const void *PTR)
Definition varatt.h:486

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

Referenced by attachPartitionTable(), and DefineRelation().

◆ cmp_partition_index_ext_dep()

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

Definition at line 23561 of file tablecmds.c.

23562{
23565
23566 if (ea->parentIndexOid != eb->parentIndexOid)
23567 return pg_cmp_u32(ea->parentIndexOid, eb->parentIndexOid);
23568 return pg_cmp_u32(ea->indexOid, eb->indexOid);
23569}
static int pg_cmp_u32(uint32 a, uint32 b)
Definition int.h:719
int b
Definition isn.c:74
int a
Definition isn.c:73

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

Referenced by collectPartitionIndexExtDeps().

◆ collectPartitionIndexExtDeps()

static List * collectPartitionIndexExtDeps ( List partitionOids)
static

Definition at line 23589 of file tablecmds.c.

23590{
23591 List *collected = NIL;
23592 List *result = NIL;
23594
23595 /*
23596 * Phase 1: collect one entry per (partition index -> parent index) pair,
23597 * with its extension dependency OIDs sorted ascending.
23598 */
23600 {
23601 Relation partRel;
23602 List *indexList;
23603
23604 /*
23605 * Use NoLock since the caller already holds AccessExclusiveLock on
23606 * these partitions.
23607 */
23608 partRel = table_open(partOid, NoLock);
23610
23611 foreach_oid(indexOid, indexList)
23612 {
23613 Oid parentIndexOid;
23615
23616 if (!get_rel_relispartition(indexOid))
23617 continue;
23618
23619 parentIndexOid = get_partition_parent(indexOid, true);
23620 if (!OidIsValid(parentIndexOid))
23621 continue;
23622
23623 entry = palloc(sizeof(PartitionIndexExtDepEntry));
23624 entry->parentIndexOid = parentIndexOid;
23625 entry->indexOid = indexOid;
23627 indexOid);
23629
23630 collected = lappend(collected, entry);
23631 }
23632
23634 table_close(partRel, NoLock);
23635 }
23636
23637 /*
23638 * Phase 2: sort by parentIndexOid so entries sharing a parent index sit
23639 * adjacent.
23640 */
23642
23643 /*
23644 * Phase 3: single linear pass verifying that adjacent entries sharing a
23645 * parent index have identical extension dependencies, and keeping one
23646 * representative entry per parent index.
23647 */
23649 {
23650 if (prev != NULL && prev->parentIndexOid == entry->parentIndexOid)
23651 {
23652 if (!equal_oid_lists(prev->extensionOids, entry->extensionOids))
23653 ereport(ERROR,
23655 errmsg("cannot merge partitions with conflicting extension dependencies"),
23656 errdetail("Partition indexes \"%s\" and \"%s\" depend on different extensions.",
23657 get_rel_name(prev->indexOid),
23658 get_rel_name(entry->indexOid))));
23659
23660 /* Duplicate entry for the same parent index; discard. */
23661 list_free(entry->extensionOids);
23662 pfree(entry);
23663 continue;
23664 }
23665
23666 result = lappend(result, entry);
23667 prev = entry;
23668 }
23669
23671
23672 return result;
23673}
uint32 result
void list_sort(List *list, list_sort_comparator cmp)
Definition list.c:1674
int list_oid_cmp(const ListCell *p1, const ListCell *p2)
Definition list.c:1703
void * palloc(Size size)
Definition mcxt.c:1390
List * getAutoExtensionsOfObject(Oid classId, Oid objectId)
Definition pg_depend.c:911
static bool equal_oid_lists(const List *a, const List *b)
static int cmp_partition_index_ext_dep(const ListCell *a, const ListCell *b)

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

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ ComputePartitionAttrs()

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

Definition at line 20517 of file tablecmds.c.

20520{
20521 int attn;
20522 ListCell *lc;
20523 Oid am_oid;
20524
20525 attn = 0;
20526 foreach(lc, partParams)
20527 {
20529 Oid atttype;
20530 Oid attcollation;
20531
20532 if (pelem->name != NULL)
20533 {
20534 /* Simple attribute reference */
20537
20539 pelem->name);
20541 ereport(ERROR,
20543 errmsg("column \"%s\" named in partition key does not exist",
20544 pelem->name),
20545 parser_errposition(pstate, pelem->location)));
20547
20548 if (attform->attnum <= 0)
20549 ereport(ERROR,
20551 errmsg("cannot use system column \"%s\" in partition key",
20552 pelem->name),
20553 parser_errposition(pstate, pelem->location)));
20554
20555 /*
20556 * Stored generated columns cannot work: They are computed after
20557 * BEFORE triggers, but partition routing is done before all
20558 * triggers. Maybe virtual generated columns could be made to
20559 * work, but then they would need to be handled as an expression
20560 * below.
20561 */
20562 if (attform->attgenerated)
20563 ereport(ERROR,
20565 errmsg("cannot use generated column in partition key"),
20566 errdetail("Column \"%s\" is a generated column.",
20567 pelem->name),
20568 parser_errposition(pstate, pelem->location)));
20569
20570 partattrs[attn] = attform->attnum;
20571 atttype = attform->atttypid;
20572 attcollation = attform->attcollation;
20574 }
20575 else
20576 {
20577 /* Expression */
20578 Node *expr = pelem->expr;
20579 char partattname[16];
20581 int i;
20582
20583 Assert(expr != NULL);
20584 atttype = exprType(expr);
20585 attcollation = exprCollation(expr);
20586
20587 /*
20588 * The expression must be of a storable type (e.g., not RECORD).
20589 * The test is the same as for whether a table column is of a safe
20590 * type (which is why we needn't check for the non-expression
20591 * case).
20592 */
20593 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
20595 atttype, attcollation,
20597
20598 /*
20599 * Strip any top-level COLLATE clause. This ensures that we treat
20600 * "x COLLATE y" and "(x COLLATE y)" alike.
20601 */
20602 while (IsA(expr, CollateExpr))
20603 expr = (Node *) ((CollateExpr *) expr)->arg;
20604
20605 /*
20606 * Examine all the columns in the partition key expression. When
20607 * the whole-row reference is present, examine all the columns of
20608 * the partitioned table.
20609 */
20610 pull_varattnos(expr, 1, &expr_attrs);
20612 {
20617 }
20618
20619 i = -1;
20620 while ((i = bms_next_member(expr_attrs, i)) >= 0)
20621 {
20623
20624 Assert(attno != 0);
20625
20626 /*
20627 * Cannot allow system column references, since that would
20628 * make partition routing impossible: their values won't be
20629 * known yet when we need to do that.
20630 */
20631 if (attno < 0)
20632 ereport(ERROR,
20634 errmsg("partition key expressions cannot contain system column references")));
20635
20636 /*
20637 * Stored generated columns cannot work: They are computed
20638 * after BEFORE triggers, but partition routing is done before
20639 * all triggers. Virtual generated columns could probably
20640 * work, but it would require more work elsewhere (for example
20641 * SET EXPRESSION would need to check whether the column is
20642 * used in partition keys). Seems safer to prohibit for now.
20643 */
20644 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
20645 ereport(ERROR,
20647 errmsg("cannot use generated column in partition key"),
20648 errdetail("Column \"%s\" is a generated column.",
20649 get_attname(RelationGetRelid(rel), attno, false)),
20650 parser_errposition(pstate, pelem->location)));
20651 }
20652
20653 if (IsA(expr, Var) &&
20654 ((Var *) expr)->varattno > 0)
20655 {
20656
20657 /*
20658 * User wrote "(column)" or "(column COLLATE something)".
20659 * Treat it like simple attribute anyway.
20660 */
20661 partattrs[attn] = ((Var *) expr)->varattno;
20662 }
20663 else
20664 {
20665 partattrs[attn] = 0; /* marks the column as expression */
20666 *partexprs = lappend(*partexprs, expr);
20667
20668 /*
20669 * transformPartitionSpec() should have already rejected
20670 * subqueries, aggregates, window functions, and SRFs, based
20671 * on the EXPR_KIND_ for partition expressions.
20672 */
20673
20674 /*
20675 * Preprocess the expression before checking for mutability.
20676 * This is essential for the reasons described in
20677 * contain_mutable_functions_after_planning. However, we call
20678 * expression_planner for ourselves rather than using that
20679 * function, because if constant-folding reduces the
20680 * expression to a constant, we'd like to know that so we can
20681 * complain below.
20682 *
20683 * Like contain_mutable_functions_after_planning, assume that
20684 * expression_planner won't scribble on its input, so this
20685 * won't affect the partexprs entry we saved above.
20686 */
20687 expr = (Node *) expression_planner((Expr *) expr);
20688
20689 /*
20690 * Partition expressions cannot contain mutable functions,
20691 * because a given row must always map to the same partition
20692 * as long as there is no change in the partition boundary
20693 * structure.
20694 */
20695 if (contain_mutable_functions(expr))
20696 ereport(ERROR,
20698 errmsg("functions in partition key expression must be marked IMMUTABLE")));
20699
20700 /*
20701 * While it is not exactly *wrong* for a partition expression
20702 * to be a constant, it seems better to reject such keys.
20703 */
20704 if (IsA(expr, Const))
20705 ereport(ERROR,
20707 errmsg("cannot use constant expression as partition key")));
20708 }
20709 }
20710
20711 /*
20712 * Apply collation override if any
20713 */
20714 if (pelem->collation)
20715 attcollation = get_collation_oid(pelem->collation, false);
20716
20717 /*
20718 * Check we have a collation iff it's a collatable type. The only
20719 * expected failures here are (1) COLLATE applied to a noncollatable
20720 * type, or (2) partition expression had an unresolved collation. But
20721 * we might as well code this to be a complete consistency check.
20722 */
20723 if (type_is_collatable(atttype))
20724 {
20725 if (!OidIsValid(attcollation))
20726 ereport(ERROR,
20728 errmsg("could not determine which collation to use for partition expression"),
20729 errhint("Use the COLLATE clause to set the collation explicitly.")));
20730 }
20731 else
20732 {
20733 if (OidIsValid(attcollation))
20734 ereport(ERROR,
20736 errmsg("collations are not supported by type %s",
20737 format_type_be(atttype))));
20738 }
20739
20740 partcollation[attn] = attcollation;
20741
20742 /*
20743 * Identify the appropriate operator class. For list and range
20744 * partitioning, we use a btree operator class; hash partitioning uses
20745 * a hash operator class.
20746 */
20747 if (strategy == PARTITION_STRATEGY_HASH)
20748 am_oid = HASH_AM_OID;
20749 else
20750 am_oid = BTREE_AM_OID;
20751
20752 if (!pelem->opclass)
20753 {
20754 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
20755
20757 {
20758 if (strategy == PARTITION_STRATEGY_HASH)
20759 ereport(ERROR,
20761 errmsg("data type %s has no default operator class for access method \"%s\"",
20762 format_type_be(atttype), "hash"),
20763 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
20764 else
20765 ereport(ERROR,
20767 errmsg("data type %s has no default operator class for access method \"%s\"",
20768 format_type_be(atttype), "btree"),
20769 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
20770 }
20771 }
20772 else
20774 atttype,
20775 am_oid == HASH_AM_OID ? "hash" : "btree",
20776 am_oid);
20777
20778 attn++;
20779 }
20780}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1425
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition bitmapset.c:1138
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition bitmapset.c:987
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:645
bool contain_mutable_functions(Node *clause)
Definition clauses.c:399
#define CHKATYPE_IS_PARTKEY
Definition heap.h:25
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition indexcmds.c:2381
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition indexcmds.c:2296
bool type_is_collatable(Oid typid)
Definition lsyscache.c:3397
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:921
#define snprintf
Definition port.h:261
#define RelationGetNumberOfAttributes(relation)
Definition rel.h:522
void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos)
Definition var.c:296

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

Referenced by DefineRelation().

◆ ConstraintImpliedByRelConstraint()

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

Definition at line 20846 of file tablecmds.c.

20847{
20849 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20850 int num_check,
20851 i;
20852
20853 num_check = (constr != NULL) ? constr->num_check : 0;
20854 for (i = 0; i < num_check; i++)
20855 {
20856 Node *cexpr;
20857
20858 /*
20859 * If this constraint hasn't been fully validated yet, we must ignore
20860 * it here.
20861 */
20862 if (!constr->check[i].ccvalid)
20863 continue;
20864
20865 /*
20866 * NOT ENFORCED constraints are always marked as invalid, which should
20867 * have been ignored.
20868 */
20869 Assert(constr->check[i].ccenforced);
20870
20871 cexpr = stringToNode(constr->check[i].ccbin);
20872
20873 /*
20874 * Run each expression through const-simplification and
20875 * canonicalization. It is necessary, because we will be comparing it
20876 * to similarly-processed partition constraint expressions, and may
20877 * fail to detect valid matches without this.
20878 */
20879 cexpr = eval_const_expressions(NULL, cexpr);
20880 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20881
20883 make_ands_implicit((Expr *) cexpr));
20884 }
20885
20886 /*
20887 * Try to make the proof. Since we are comparing CHECK constraints, we
20888 * need to use weak implication, i.e., we assume existConstraint is
20889 * not-false and try to prove the same for testConstraint.
20890 *
20891 * Note that predicate_implied_by assumes its first argument is known
20892 * immutable. That should always be true for both NOT NULL and partition
20893 * constraints, so we don't test it here.
20894 */
20896}
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 18179 of file tablecmds.c.

18180{
18183
18184 if (acon->condeferrable != bcon->condeferrable ||
18185 acon->condeferred != bcon->condeferred ||
18188 return false;
18189 else
18190 return true;
18191}
static char * decompile_conbin(HeapTuple contup, TupleDesc tupdesc)

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

Referenced by ATCheckCheckConstrHasEnforcedParent(), and MergeConstraintsIntoExisting().

◆ CreateFKCheckTrigger()

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

Definition at line 14331 of file tablecmds.c.

14334{
14337
14338 /*
14339 * Note: for a self-referential FK (referencing and referenced tables are
14340 * the same), it is important that the ON UPDATE action fires before the
14341 * CHECK action, since both triggers will fire on the same row during an
14342 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
14343 * state of the row. Triggers fire in name order, so we ensure this by
14344 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
14345 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
14346 */
14348 fk_trigger->replace = false;
14349 fk_trigger->isconstraint = true;
14350 fk_trigger->trigname = "RI_ConstraintTrigger_c";
14351 fk_trigger->relation = NULL;
14352
14353 /* Either ON INSERT or ON UPDATE */
14354 if (on_insert)
14355 {
14356 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
14358 }
14359 else
14360 {
14361 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
14363 }
14364
14365 fk_trigger->args = NIL;
14366 fk_trigger->row = true;
14368 fk_trigger->columns = NIL;
14369 fk_trigger->whenClause = NULL;
14370 fk_trigger->transitionRels = NIL;
14371 fk_trigger->deferrable = fkconstraint->deferrable;
14372 fk_trigger->initdeferred = fkconstraint->initdeferred;
14373 fk_trigger->constrrel = NULL;
14374
14376 constraintOid, indexOid, InvalidOid,
14377 parentTrigOid, NULL, true, false);
14378
14379 /* Make changes-so-far visible */
14381
14382 return trigAddress.objectId;
14383}
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 14394 of file tablecmds.c.

14398{
14401
14402 /*
14403 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14404 * DELETE action on the referenced table.
14405 */
14407 fk_trigger->replace = false;
14408 fk_trigger->isconstraint = true;
14409 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14410 fk_trigger->relation = NULL;
14411 fk_trigger->args = NIL;
14412 fk_trigger->row = true;
14415 fk_trigger->columns = NIL;
14416 fk_trigger->whenClause = NULL;
14417 fk_trigger->transitionRels = NIL;
14418 fk_trigger->constrrel = NULL;
14419
14420 switch (fkconstraint->fk_del_action)
14421 {
14423 fk_trigger->deferrable = fkconstraint->deferrable;
14424 fk_trigger->initdeferred = fkconstraint->initdeferred;
14425 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
14426 break;
14428 fk_trigger->deferrable = false;
14429 fk_trigger->initdeferred = false;
14430 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
14431 break;
14433 fk_trigger->deferrable = false;
14434 fk_trigger->initdeferred = false;
14435 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
14436 break;
14438 fk_trigger->deferrable = false;
14439 fk_trigger->initdeferred = false;
14440 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
14441 break;
14443 fk_trigger->deferrable = false;
14444 fk_trigger->initdeferred = false;
14445 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
14446 break;
14447 default:
14448 elog(ERROR, "unrecognized FK action type: %d",
14449 (int) fkconstraint->fk_del_action);
14450 break;
14451 }
14452
14454 constraintOid, indexOid, InvalidOid,
14455 parentDelTrigger, NULL, true, false);
14456 if (deleteTrigOid)
14457 *deleteTrigOid = trigAddress.objectId;
14458
14459 /* Make changes-so-far visible */
14461
14462 /*
14463 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14464 * UPDATE action on the referenced table.
14465 */
14467 fk_trigger->replace = false;
14468 fk_trigger->isconstraint = true;
14469 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14470 fk_trigger->relation = NULL;
14471 fk_trigger->args = NIL;
14472 fk_trigger->row = true;
14475 fk_trigger->columns = NIL;
14476 fk_trigger->whenClause = NULL;
14477 fk_trigger->transitionRels = NIL;
14478 fk_trigger->constrrel = NULL;
14479
14480 switch (fkconstraint->fk_upd_action)
14481 {
14483 fk_trigger->deferrable = fkconstraint->deferrable;
14484 fk_trigger->initdeferred = fkconstraint->initdeferred;
14485 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
14486 break;
14488 fk_trigger->deferrable = false;
14489 fk_trigger->initdeferred = false;
14490 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
14491 break;
14493 fk_trigger->deferrable = false;
14494 fk_trigger->initdeferred = false;
14495 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
14496 break;
14498 fk_trigger->deferrable = false;
14499 fk_trigger->initdeferred = false;
14500 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
14501 break;
14503 fk_trigger->deferrable = false;
14504 fk_trigger->initdeferred = false;
14505 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
14506 break;
14507 default:
14508 elog(ERROR, "unrecognized FK action type: %d",
14509 (int) fkconstraint->fk_upd_action);
14510 break;
14511 }
14512
14514 constraintOid, indexOid, InvalidOid,
14515 parentUpdTrigger, NULL, true, false);
14516 if (updateTrigOid)
14517 *updateTrigOid = trigAddress.objectId;
14518}
#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 14529 of file tablecmds.c.

14534{
14536 constraintOid, indexOid,
14537 parentInsTrigger, true);
14539 constraintOid, indexOid,
14540 parentUpdTrigger, false);
14541}
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 18082 of file tablecmds.c.

18083{
18085 SysScanDesc scan;
18089
18090 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
18092
18093 /*
18094 * Check for duplicates in the list of parents, and determine the highest
18095 * inhseqno already present; we'll use the next one for the new parent.
18096 * Also, if proposed child is a partition, it cannot already be
18097 * inheriting.
18098 *
18099 * Note: we do not reject the case where the child already inherits from
18100 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
18101 */
18102 ScanKeyInit(&key,
18107 true, NULL, 1, &key);
18108
18109 /* inhseqno sequences start at 1 */
18110 inhseqno = 0;
18112 {
18114
18115 if (inh->inhparent == RelationGetRelid(parent_rel))
18116 ereport(ERROR,
18118 errmsg("relation \"%s\" would be inherited from more than once",
18120
18121 if (inh->inhseqno > inhseqno)
18122 inhseqno = inh->inhseqno;
18123 }
18124 systable_endscan(scan);
18125
18126 /* Match up the columns and bump attinhcount as needed */
18128
18129 /* Match up the constraints and bump coninhcount as needed */
18131
18132 /*
18133 * OK, it looks valid. Make the catalog entries that show inheritance.
18134 */
18137 inhseqno + 1,
18139 parent_rel->rd_rel->relkind ==
18141
18142 /* Now we're done with pg_inherits */
18144}
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:3644
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 23211 of file tablecmds.c.

23213{
23215 Oid newRelId;
23219 List *colList = NIL;
23220 Oid relamId;
23224
23225 /* If the existing rel is temp, it must belong to this session. */
23227 ereport(ERROR,
23229 errmsg("cannot create as partition of temporary relation of another session"));
23230
23231 /* Look up inheritance ancestors and generate the relation schema. */
23233
23234 /* Create a tuple descriptor from the relation schema. */
23236
23237 /* Look up the access method for the new relation. */
23239
23240 /* Look up the namespace in which we are supposed to create the relation. */
23241 namespaceId =
23244 ereport(ERROR,
23246 errmsg("relation \"%s\" already exists", newPartName->relname));
23247
23248 /*
23249 * We intended to create the partition with the same persistence as the
23250 * parent table, but we still need to recheck because that might be
23251 * affected by the search_path. If the parent is permanent, so must be
23252 * all of its partitions.
23253 */
23254 if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
23255 newPartName->relpersistence == RELPERSISTENCE_TEMP)
23256 ereport(ERROR,
23258 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
23260
23261 /* Permanent rels cannot be partitions belonging to a temporary parent. */
23262 if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
23263 parent_relform->relpersistence == RELPERSISTENCE_TEMP)
23264 ereport(ERROR,
23266 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
23268
23269 /*
23270 * Select the tablespace for the new partition. Mirror the logic that
23271 * CREATE TABLE foo PARTITION OF ... uses in DefineRelation: take the
23272 * partitioned parent's explicit tablespace if it has one, otherwise take
23273 * default_tablespace into account, and finally use the database default.
23274 */
23275 tablespaceId = parent_relform->reltablespace;
23277 tablespaceId = GetDefaultTablespace(newPartName->relpersistence, false);
23278
23279 /* Check permissions except when using database's default */
23281 {
23283
23286 if (aclresult != ACLCHECK_OK)
23289 }
23290
23291 /* In all cases disallow placing user relations in pg_global */
23293 ereport(ERROR,
23295 errmsg("only shared relations can be placed in pg_global tablespace")));
23296
23297 /* Create the relation. */
23301 InvalidOid,
23302 InvalidOid,
23303 InvalidOid,
23304 ownerId,
23305 relamId,
23306 descriptor,
23307 NIL,
23309 newPartName->relpersistence,
23310 false,
23311 false,
23313 (Datum) 0,
23314 true,
23316 false, /* is_internal */
23317 InvalidOid,
23318 NULL);
23319
23320 /*
23321 * We must bump the command counter to make the newly-created relation
23322 * tuple visible for opening.
23323 */
23325
23326 /*
23327 * Create a TOAST table if the table needs one. MERGE/SPLIT PARTITION
23328 * moves rows from existing partition(s) into new partition(s), which may
23329 * carry out-of-line varlena values that the new relation must be able to
23330 * store. Also, the new partition must be able to receive out-of-line
23331 * varlena values after the DDL operation is complete.
23332 */
23334
23335 /*
23336 * Open the new partition with no lock, because we already have an
23337 * AccessExclusiveLock placed there after creation.
23338 */
23340
23341 /* Find or create a work queue entry for the newly created table. */
23343
23344 /* Create constraints, default values, and generated values. */
23346
23347 /*
23348 * Need to call CommandCounterIncrement, so a fresh relcache entry has
23349 * newly installed constraint info.
23350 */
23352
23353 return newRel;
23354}
Oid GetDefaultTablespace(char relpersistence, bool partitioned)
Oid heap_create_with_catalog(const char *relname, Oid relnamespace, Oid reltablespace, Oid relid, Oid reltypeid, Oid reloftypeid, Oid ownerid, Oid accessmtd, TupleDesc tupdesc, List *cooked_constraints, char relkind, char relpersistence, bool shared_relation, bool mapped_relation, OnCommitAction oncommit, Datum reloptions, bool use_user_acl, bool allow_system_table_mods, bool is_internal, Oid relrewrite, ObjectAddress *typaddress)
Definition heap.c:1140
@ ONCOMMIT_NOOP
Definition primnodes.h:59
static void createTableConstraints(List **wqueue, AlteredTableInfo *tab, Relation parent_rel, Relation newRel)
static List * getAttributesList(Relation parent_rel)
void NewRelationCreateToastTable(Oid relOid, Datum reloptions)
Definition toasting.c:72

References ACL_CREATE, aclcheck_error(), ACLCHECK_OK, allowSystemTableMods, ATGetQueueEntry(), BuildDescForRelation(), CommandCounterIncrement(), createTableConstraints(), ereport, errcode(), errmsg, ERROR, fb(), get_tablespace_name(), getAttributesList(), GetDefaultTablespace(), GetUserId(), heap_create_with_catalog(), InvalidOid, MyDatabaseTableSpace, NewRelationCreateToastTable(), NIL, NoLock, object_aclcheck(), OBJECT_TABLESPACE, 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 23973 of file tablecmds.c.

23974{
23976
23978 pc->partRel = partRel;
23979
23980 /*
23981 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
23982 * don't bother using it.
23983 */
23984 pc->bistate = GetBulkInsertState();
23985
23986 /* Create a destination tuple slot for the new partition. */
23987 pc->dstslot = table_slot_create(pc->partRel, NULL);
23988
23989 return pc;
23990}
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 23004 of file tablecmds.c.

23006{
23008 TupleConstr *constr;
23009 AttrMap *attmap;
23011 int ccnum;
23012 List *constraints = NIL;
23014
23016 constr = tupleDesc->constr;
23017
23018 if (!constr)
23019 return;
23020
23021 /*
23022 * Construct a map from the parent relation's attnos to the child rel's.
23023 * This re-checks type match, etc, although it shouldn't be possible to
23024 * have a failure since both tables are locked.
23025 */
23027 tupleDesc,
23028 false);
23029
23030 /* Cycle for default values. */
23031 for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
23032 {
23034 parent_attno - 1);
23035
23036 /* Ignore dropped columns in the parent. */
23037 if (attribute->attisdropped)
23038 continue;
23039
23040 /* Copy the default, if present, and it should be copied. */
23041 if (attribute->atthasdef)
23042 {
23044 bool found_whole_row;
23045 AttrNumber num;
23046 Node *def;
23048
23049 if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
23051 else
23052 {
23054 if (this_default == NULL)
23055 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
23057 }
23058
23059 num = attmap->attnums[parent_attno - 1];
23060 def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
23061
23062 if (found_whole_row && attribute->attgenerated != '\0')
23063 elog(ERROR, "cannot convert whole-row table reference");
23064
23065 /* Add a pre-cooked default expression. */
23066 StoreAttrDefault(newRel, num, def, false);
23067
23068 /*
23069 * Stored generated column expressions in parent_rel might
23070 * reference the tableoid. newRel, parent_rel tableoid clear is
23071 * not the same. If so, these stored generated columns require
23072 * recomputation for newRel within MergePartitionsMoveRows.
23073 */
23074 if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
23075 {
23077 newval->attnum = num;
23078 newval->expr = expression_planner((Expr *) def);
23079 newval->is_generated = (attribute->attgenerated != '\0');
23080 tab->newvals = lappend(tab->newvals, newval);
23081 }
23082 }
23083 }
23084
23085 /* Cycle for CHECK constraints. */
23086 for (ccnum = 0; ccnum < constr->num_check; ccnum++)
23087 {
23088 char *ccname = constr->check[ccnum].ccname;
23089 char *ccbin = constr->check[ccnum].ccbin;
23090 bool ccenforced = constr->check[ccnum].ccenforced;
23091 bool ccnoinherit = constr->check[ccnum].ccnoinherit;
23092 bool ccvalid = constr->check[ccnum].ccvalid;
23094 bool found_whole_row;
23095 Constraint *con;
23096
23097 /*
23098 * The partitioned table can not have a NO INHERIT check constraint
23099 * (see StoreRelCheck function for details).
23100 */
23101 Assert(!ccnoinherit);
23102
23104 1, 0,
23105 attmap,
23106 InvalidOid, &found_whole_row);
23107
23108 /*
23109 * For the moment we have to reject whole-row variables (as for CREATE
23110 * TABLE LIKE and inheritances).
23111 */
23112 if (found_whole_row)
23113 elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
23114 ccname,
23116
23117 con = makeNode(Constraint);
23118 con->contype = CONSTR_CHECK;
23119 con->conname = pstrdup(ccname);
23120 con->deferrable = false;
23121 con->initdeferred = false;
23122 con->is_enforced = ccenforced;
23123 con->skip_validation = !ccvalid;
23124 con->initially_valid = ccvalid;
23125 con->is_no_inherit = ccnoinherit;
23126 con->raw_expr = NULL;
23128 con->location = -1;
23129 constraints = lappend(constraints, con);
23130 }
23131
23132 /* Install all CHECK constraints. */
23134 false, true, false, NULL);
23135
23136 /* Make the additional catalog changes visible. */
23138
23139 /*
23140 * parent_rel check constraint expression may reference tableoid, so later
23141 * in MergePartitionsMoveRows, we need to evaluate the check constraint
23142 * again for the newRel. We can check whether the check constraint
23143 * contains a tableoid reference via pull_varattnos.
23144 */
23146 {
23147 if (!ccon->skip_validation)
23148 {
23149 Node *qual;
23150 Bitmapset *attnums = NULL;
23151
23152 Assert(ccon->contype == CONSTR_CHECK);
23154 pull_varattnos(qual, 1, &attnums);
23155
23156 /*
23157 * Add a check only if it contains a tableoid
23158 * (TableOidAttributeNumber).
23159 */
23161 attnums))
23162 {
23164
23166 newcon->name = ccon->name;
23167 newcon->contype = CONSTR_CHECK;
23168 newcon->qual = qual;
23169
23170 tab->constraints = lappend(tab->constraints, newcon);
23171 }
23172 }
23173 }
23174
23175 /* Don't need the cookedConstraints anymore. */
23177
23178 /* Reproduce not-null constraints. */
23179 if (constr->has_not_null)
23180 {
23181 List *nnconstraints;
23182
23183 /*
23184 * The "include_noinh" argument is false because a partitioned table
23185 * can't have NO INHERIT constraint.
23186 */
23188 false, false);
23189
23190 Assert(list_length(nnconstraints) > 0);
23191
23192 /*
23193 * We already set pg_attribute.attnotnull in createPartitionTable. No
23194 * need call set_attnotnull again.
23195 */
23196 AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, false, NULL);
23197 }
23198}
void list_free_deep(List *list)
Definition list.c:1560
char * nodeToString(const void *obj)
Definition outfuncs.c:811
List * RelationGetNotNullConstraints(Oid relid, bool cooked, bool include_noinh)
Node * build_generation_expression(Relation rel, int attrno)
char * ccname
Definition tupdesc.h:30
bool ccnoinherit
Definition tupdesc.h:34
bool initdeferred
ParseLoc location
bool is_enforced
char * cooked_expr
bool initially_valid
bool deferrable
Node * raw_expr
bool has_not_null
Definition tupdesc.h:45
#define TableOidAttributeNumber
Definition sysattr.h:26
Node * TupleDescGetDefault(TupleDesc tupdesc, AttrNumber attnum)
Definition tupdesc.c:1161

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

Referenced by createPartitionTable().

◆ decompile_conbin()

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

Definition at line 18151 of file tablecmds.c.

18152{
18154 bool isnull;
18155 Datum attr;
18156 Datum expr;
18157
18159 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
18160 if (isnull)
18161 elog(ERROR, "null conbin for constraint %u", con->oid);
18162
18163 expr = DirectFunctionCall2(pg_get_expr, attr,
18164 ObjectIdGetDatum(con->conrelid));
18165 return TextDatumGetCString(expr);
18166}
#define DirectFunctionCall2(func, arg1, arg2)
Definition fmgr.h:690
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition ruleutils.c:3035

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

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

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

23997{
23998 ListCell *ltab;
23999
24001 FreeBulkInsertState(pc->bistate);
24002
24003 table_finish_bulk_insert(pc->partRel, ti_options);
24004
24005 /*
24006 * We don't need to process this pc->partRel so delete the ALTER TABLE
24007 * queue of it.
24008 */
24009 foreach(ltab, *wqueue)
24010 {
24012
24013 if (tab->relid == RelationGetRelid(pc->partRel))
24014 {
24016 break;
24017 }
24018 }
24019
24020 pfree(pc);
24021}
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 21872 of file tablecmds.c.

21874{
21876 List *fks;
21877 ListCell *cell;
21878 List *indexes;
21882 HeapTuple tuple,
21883 newtuple;
21885 List *fkoids = NIL;
21886
21887 if (concurrent)
21888 {
21889 /*
21890 * We can remove the pg_inherits row now. (In the non-concurrent case,
21891 * this was already done).
21892 */
21893 RemoveInheritance(partRel, rel, true);
21894 }
21895
21896 /* Drop any triggers that were cloned on creation/attach. */
21898
21899 /*
21900 * Detach any foreign keys that are inherited. This includes creating
21901 * additional action triggers.
21902 */
21904 if (fks != NIL)
21906
21907 /*
21908 * It's possible that the partition being detached has a foreign key that
21909 * references a partitioned table. When that happens, there are multiple
21910 * pg_constraint rows for the partition: one points to the partitioned
21911 * table itself, while the others point to each of its partitions. Only
21912 * the topmost one is to be considered here; the child constraints must be
21913 * left alone, because conceptually those aren't coming from our parent
21914 * partitioned table, but from this partition itself.
21915 *
21916 * We implement this by collecting all the constraint OIDs in a first scan
21917 * of the FK array, and skipping in the loop below those constraints whose
21918 * parents are listed here.
21919 */
21921 fkoids = lappend_oid(fkoids, fk->conoid);
21922
21923 foreach(cell, fks)
21924 {
21925 ForeignKeyCacheInfo *fk = lfirst(cell);
21928
21931 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21933
21934 /*
21935 * Consider only inherited foreign keys, and only if their parents
21936 * aren't in the list.
21937 */
21938 if (conform->contype != CONSTRAINT_FOREIGN ||
21939 !OidIsValid(conform->conparentid) ||
21940 list_member_oid(fkoids, conform->conparentid))
21941 {
21943 continue;
21944 }
21945
21946 /*
21947 * The constraint on this table must be marked no longer a child of
21948 * the parent's constraint, as do its check triggers.
21949 */
21951
21952 /*
21953 * Also, look up the partition's "check" triggers corresponding to the
21954 * ENFORCED constraint being detached and detach them from the parent
21955 * triggers. NOT ENFORCED constraints do not have these triggers;
21956 * therefore, this step is not needed.
21957 */
21958 if (fk->conenforced)
21959 {
21962
21964 fk->conoid, fk->confrelid, fk->conrelid,
21968 RelationGetRelid(partRel));
21971 RelationGetRelid(partRel));
21972 }
21973
21974 /*
21975 * Lastly, create the action triggers on the referenced table, using
21976 * addFkRecurseReferenced, which requires some elaborate setup (so put
21977 * it in a separate block). While at it, if the table is partitioned,
21978 * that function will recurse to create the pg_constraint rows and
21979 * action triggers for each partition.
21980 *
21981 * Note there's no need to do addFkConstraint() here, because the
21982 * pg_constraint row already exists.
21983 */
21984 {
21986 int numfks;
21992 int numfkdelsetcols;
21993 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21995
21997 &numfks,
21998 conkey,
21999 confkey,
22000 conpfeqop,
22001 conppeqop,
22002 conffeqop,
22004 confdelsetcols);
22005
22006 /* Create a synthetic node we'll use throughout */
22009 fkconstraint->conname = pstrdup(NameStr(conform->conname));
22010 fkconstraint->deferrable = conform->condeferrable;
22011 fkconstraint->initdeferred = conform->condeferred;
22012 fkconstraint->is_enforced = conform->conenforced;
22013 fkconstraint->skip_validation = true;
22014 fkconstraint->initially_valid = conform->convalidated;
22015 /* a few irrelevant fields omitted here */
22016 fkconstraint->pktable = NULL;
22017 fkconstraint->fk_attrs = NIL;
22018 fkconstraint->pk_attrs = NIL;
22019 fkconstraint->fk_matchtype = conform->confmatchtype;
22020 fkconstraint->fk_upd_action = conform->confupdtype;
22021 fkconstraint->fk_del_action = conform->confdeltype;
22022 fkconstraint->fk_del_set_cols = NIL;
22023 fkconstraint->old_conpfeqop = NIL;
22024 fkconstraint->old_pktable_oid = InvalidOid;
22025 fkconstraint->location = -1;
22026
22027 /* set up colnames, used to generate the constraint name */
22028 for (int i = 0; i < numfks; i++)
22029 {
22031
22032 att = TupleDescAttr(RelationGetDescr(partRel),
22033 conkey[i] - 1);
22034
22035 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
22036 makeString(NameStr(att->attname)));
22037 }
22038
22040
22042 refdRel,
22043 conform->conindid,
22044 fk->conoid,
22045 numfks,
22046 confkey,
22047 conkey,
22048 conpfeqop,
22049 conppeqop,
22050 conffeqop,
22052 confdelsetcols,
22053 true,
22055 conform->conperiod);
22056 table_close(refdRel, NoLock); /* keep lock till end of xact */
22057 }
22058
22060 }
22062 if (trigrel)
22064
22065 /*
22066 * Any sub-constraints that are in the referenced-side of a larger
22067 * constraint have to be removed. This partition is no longer part of the
22068 * key space of the constraint.
22069 */
22070 foreach(cell, GetParentedForeignKeyRefs(partRel))
22071 {
22072 Oid constrOid = lfirst_oid(cell);
22073 ObjectAddress constraint;
22074
22077 constrOid,
22081
22083 performDeletion(&constraint, DROP_RESTRICT, 0);
22084 }
22085
22086 /* Now we can detach indexes */
22087 indexes = RelationGetIndexList(partRel);
22088 foreach(cell, indexes)
22089 {
22090 Oid idxid = lfirst_oid(cell);
22091 Oid parentidx;
22092 Relation idx;
22093 Oid constrOid;
22095
22096 if (!has_superclass(idxid))
22097 continue;
22098
22099 parentidx = get_partition_parent(idxid, false);
22100 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
22101
22104
22105 /*
22106 * If there's a constraint associated with the index, detach it too.
22107 * Careful: it is possible for a constraint index in a partition to be
22108 * the child of a non-constraint index, so verify whether the parent
22109 * index does actually have a constraint.
22110 */
22112 idxid);
22114 parentidx);
22117
22119 }
22120
22121 /* Update pg_class tuple */
22125 if (!HeapTupleIsValid(tuple))
22126 elog(ERROR, "cache lookup failed for relation %u",
22127 RelationGetRelid(partRel));
22129
22130 /* Clear relpartbound and reset relispartition */
22131 memset(new_val, 0, sizeof(new_val));
22132 memset(new_null, false, sizeof(new_null));
22133 memset(new_repl, false, sizeof(new_repl));
22137 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
22139
22140 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
22141 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
22142 heap_freetuple(newtuple);
22144
22145 /*
22146 * Drop identity property from all identity columns of partition.
22147 */
22148 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
22149 {
22150 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
22151
22152 if (!attr->attisdropped && attr->attidentity)
22153 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
22154 AccessExclusiveLock, true, true);
22155 }
22156
22158 {
22159 /*
22160 * If the relation being detached is the default partition itself,
22161 * remove it from the parent's pg_partitioned_table entry.
22162 *
22163 * If not, we must invalidate default partition's relcache entry, as
22164 * in StorePartitionBound: its partition constraint depends on every
22165 * other partition's partition constraint.
22166 */
22167 if (RelationGetRelid(partRel) == defaultPartOid)
22169 else
22171 }
22172
22173 /*
22174 * Invalidate the parent's relcache so that the partition is no longer
22175 * included in its partition descriptor.
22176 */
22178
22179 /*
22180 * If the partition we just detached is partitioned itself, invalidate
22181 * relcache for all descendent partitions too to ensure that their
22182 * rd_partcheck expression trees are rebuilt; must lock partitions before
22183 * doing so, using the same lockmode as what partRel has been locked with
22184 * by the caller.
22185 */
22186 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
22187 {
22188 List *children;
22189
22190 children = find_all_inheritors(RelationGetRelid(partRel),
22192 foreach(cell, children)
22193 {
22195 }
22196 }
22197}
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 23517 of file tablecmds.c.

23518{
23519 /* Remove the pg_inherits row first. */
23521
23522 /*
23523 * Detaching the partition might involve TOAST table access, so ensure we
23524 * have a valid snapshot.
23525 */
23527
23528 /* Do the final part of detaching. */
23530
23532}

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

18869{
18871 SysScanDesc scan;
18872 ScanKeyData key[3];
18874
18876
18877 ScanKeyInit(&key[0],
18881 ScanKeyInit(&key[1],
18884 ObjectIdGetDatum(relid));
18885 ScanKeyInit(&key[2],
18888 Int32GetDatum(0));
18889
18891 NULL, 3, key);
18892
18894 {
18896
18897 if (dep->refclassid == refclassid &&
18898 dep->refobjid == refobjid &&
18899 dep->refobjsubid == 0 &&
18900 dep->deptype == deptype)
18902 }
18903
18904 systable_endscan(scan);
18906}

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

22241{
22243 SysScanDesc scan;
22246 ObjectAddresses *objects;
22247
22248 objects = new_object_addresses();
22249
22250 /*
22251 * Scan pg_trigger to search for all triggers on this rel.
22252 */
22257 true, NULL, 1, &skey);
22259 {
22262
22263 /* Ignore triggers that weren't cloned */
22264 if (!OidIsValid(pg_trigger->tgparentid))
22265 continue;
22266
22267 /*
22268 * Ignore internal triggers that are implementation objects of foreign
22269 * keys, because these will be detached when the foreign keys
22270 * themselves are.
22271 */
22272 if (OidIsValid(pg_trigger->tgconstrrelid))
22273 continue;
22274
22275 /*
22276 * This is ugly, but necessary: remove the dependency markings on the
22277 * trigger so that it can be removed.
22278 */
22285
22286 /* remember this trigger to remove it below */
22288 add_exact_object_address(&trig, objects);
22289 }
22290
22291 /* make the dependency removal visible to the deletion below */
22294
22295 /* done */
22296 free_object_addresses(objects);
22297 systable_endscan(scan);
22299}

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

14617{
14621 List *children;
14622 bool is_no_inherit_constraint = false;
14623 char *constrName;
14624 char *colname = NULL;
14625
14626 /* Guard against stack overflow due to overly deep inheritance tree. */
14628
14629 /* At top level, permission check was done in ATPrepCmd, else do it */
14630 if (recursing)
14633
14635
14637 constrName = NameStr(con->conname);
14638
14639 /* Don't allow drop of inherited constraints */
14640 if (con->coninhcount > 0 && !recursing)
14641 ereport(ERROR,
14643 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14645
14646 /*
14647 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14648 *
14649 * While doing that, we're in a good position to disallow dropping a not-
14650 * null constraint underneath a primary key, a replica identity index, or
14651 * a generated identity column.
14652 */
14653 if (con->contype == CONSTRAINT_NOTNULL)
14654 {
14661
14662 /* save column name for recursion step */
14663 colname = get_attname(RelationGetRelid(rel), attnum, false);
14664
14665 /*
14666 * Disallow if it's in the primary key. For partitioned tables we
14667 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14668 * return NULL if the primary key is invalid; but we still need to
14669 * protect not-null constraints under such a constraint, so check the
14670 * slow way.
14671 */
14673
14674 if (pkattrs == NULL &&
14675 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14676 {
14678
14679 if (OidIsValid(pkindex))
14680 {
14682
14683 pkattrs = NULL;
14684 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14685 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14686
14688 }
14689 }
14690
14691 if (pkattrs &&
14693 ereport(ERROR,
14695 errmsg("column \"%s\" is in a primary key",
14696 get_attname(RelationGetRelid(rel), attnum, false)));
14697
14698 /* Disallow if it's in the replica identity */
14701 ereport(ERROR,
14703 errmsg("column \"%s\" is in index used as replica identity",
14704 get_attname(RelationGetRelid(rel), attnum, false)));
14705
14706 /* Disallow if it's a GENERATED AS IDENTITY column */
14709 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14710 attnum, RelationGetRelid(rel));
14712 if (attForm->attidentity != '\0')
14713 ereport(ERROR,
14715 errmsg("column \"%s\" of relation \"%s\" is an identity column",
14717 false),
14719
14720 /* All good -- reset attnotnull if needed */
14721 if (attForm->attnotnull)
14722 {
14723 attForm->attnotnull = false;
14725 }
14726
14728 }
14729
14730 is_no_inherit_constraint = con->connoinherit;
14731
14732 /*
14733 * If it's a foreign-key constraint, we'd better lock the referenced table
14734 * and check that that's not in use, just as we've already done for the
14735 * constrained table (else we might, eg, be dropping a trigger that has
14736 * unfired events). But we can/must skip that in the self-referential
14737 * case.
14738 */
14739 if (con->contype == CONSTRAINT_FOREIGN &&
14740 con->confrelid != RelationGetRelid(rel))
14741 {
14742 Relation frel;
14743
14744 /* Must match lock taken by RemoveTriggerById: */
14745 frel = table_open(con->confrelid, AccessExclusiveLock);
14748 }
14749
14750 /*
14751 * Perform the actual constraint deletion
14752 */
14754 performDeletion(&conobj, behavior, 0);
14755
14756 /*
14757 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14758 * are dropped via the dependency mechanism, so we're done here.
14759 */
14760 if (con->contype != CONSTRAINT_CHECK &&
14761 con->contype != CONSTRAINT_NOTNULL &&
14762 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14763 {
14765 return conobj;
14766 }
14767
14768 /*
14769 * Propagate to children as appropriate. Unlike most other ALTER
14770 * routines, we have to do this one level of recursion at a time; we can't
14771 * use find_all_inheritors to do it in one pass.
14772 */
14774 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14775 else
14776 children = NIL;
14777
14778 foreach_oid(childrelid, children)
14779 {
14781 HeapTuple tuple;
14783
14784 /* find_inheritance_children already got lock */
14787
14788 /*
14789 * We search for not-null constraints by column name, and others by
14790 * constraint name.
14791 */
14792 if (con->contype == CONSTRAINT_NOTNULL)
14793 {
14794 tuple = findNotNullConstraint(childrelid, colname);
14795 if (!HeapTupleIsValid(tuple))
14796 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14797 colname, RelationGetRelid(childrel));
14798 }
14799 else
14800 {
14801 SysScanDesc scan;
14802 ScanKeyData skey[3];
14803
14804 ScanKeyInit(&skey[0],
14808 ScanKeyInit(&skey[1],
14812 ScanKeyInit(&skey[2],
14817 true, NULL, 3, skey);
14818 /* There can only be one, so no need to loop */
14819 tuple = systable_getnext(scan);
14820 if (!HeapTupleIsValid(tuple))
14821 ereport(ERROR,
14823 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14824 constrName,
14826 tuple = heap_copytuple(tuple);
14827 systable_endscan(scan);
14828 }
14829
14831
14832 /* Right now only CHECK and not-null constraints can be inherited */
14833 if (childcon->contype != CONSTRAINT_CHECK &&
14834 childcon->contype != CONSTRAINT_NOTNULL)
14835 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14836
14837 if (childcon->coninhcount <= 0) /* shouldn't happen */
14838 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14839 childrelid, NameStr(childcon->conname));
14840
14841 if (recurse)
14842 {
14843 /*
14844 * If the child constraint has other definition sources, just
14845 * decrement its inheritance count; if not, recurse to delete it.
14846 */
14847 if (childcon->coninhcount == 1 && !childcon->conislocal)
14848 {
14849 /* Time to delete this child constraint, too */
14850 dropconstraint_internal(childrel, tuple, behavior,
14851 recurse, true, missing_ok,
14852 lockmode);
14853 }
14854 else
14855 {
14856 /* Child constraint must survive my deletion */
14857 childcon->coninhcount--;
14858 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14859
14860 /* Make update visible */
14862 }
14863 }
14864 else
14865 {
14866 /*
14867 * If we were told to drop ONLY in this table (no recursion) and
14868 * there are no further parents for this constraint, we need to
14869 * mark the inheritors' constraints as locally defined rather than
14870 * inherited.
14871 */
14872 childcon->coninhcount--;
14873 if (childcon->coninhcount == 0)
14874 childcon->conislocal = true;
14875
14876 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14877
14878 /* Make update visible */
14880 }
14881
14882 heap_freetuple(tuple);
14883
14885 }
14886
14888
14889 return conobj;
14890}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:934
Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
Definition relcache.c:5059
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition relcache.c:5315
@ INDEX_ATTR_BITMAP_PRIMARY_KEY
Definition relcache.h:71
@ INDEX_ATTR_BITMAP_IDENTITY_KEY
Definition relcache.h:72
HeapTuple SearchSysCacheCopyAttNum(Oid relid, int16 attnum)
Definition syscache.c:562

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

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

◆ DropErrorMsgNonExistent()

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

Definition at line 1522 of file tablecmds.c.

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

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

Referenced by RemoveRelations().

◆ DropErrorMsgWrongType()

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

Definition at line 1570 of file tablecmds.c.

1571{
1572 const struct dropmsgstrings *rentry;
1573 const struct dropmsgstrings *wentry;
1574
1575 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1576 if (rentry->kind == rightkind)
1577 break;
1578 Assert(rentry->kind != '\0');
1579
1580 for (wentry = dropmsgstringarray; wentry->kind != '\0'; wentry++)
1581 if (wentry->kind == wrongkind)
1582 break;
1583 /* wrongkind could be something we don't have in our table... */
1584
1585 ereport(ERROR,
1587 errmsg(rentry->nota_msg, relname),
1588 (wentry->kind != '\0') ? errhint("%s", _(wentry->drophint_msg)) : 0));
1589}

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

12153{
12155 SysScanDesc scan;
12157
12158 ScanKeyInit(&key,
12161 ObjectIdGetDatum(conoid));
12163 NULL, 1, &key);
12164 while ((trigtup = systable_getnext(scan)) != NULL)
12165 {
12168
12169 /* Invalid if trigger is not for a referential integrity constraint */
12170 if (!OidIsValid(trgform->tgconstrrelid))
12171 continue;
12172 if (OidIsValid(conrelid) && trgform->tgconstrrelid != conrelid)
12173 continue;
12174 if (OidIsValid(confrelid) && trgform->tgrelid != confrelid)
12175 continue;
12176
12177 /* We should be dropping trigger related to foreign key constraint */
12178 Assert(trgform->tgfoid == F_RI_FKEY_CHECK_INS ||
12179 trgform->tgfoid == F_RI_FKEY_CHECK_UPD ||
12180 trgform->tgfoid == F_RI_FKEY_CASCADE_DEL ||
12181 trgform->tgfoid == F_RI_FKEY_CASCADE_UPD ||
12182 trgform->tgfoid == F_RI_FKEY_RESTRICT_DEL ||
12183 trgform->tgfoid == F_RI_FKEY_RESTRICT_UPD ||
12184 trgform->tgfoid == F_RI_FKEY_SETNULL_DEL ||
12185 trgform->tgfoid == F_RI_FKEY_SETNULL_UPD ||
12186 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_DEL ||
12187 trgform->tgfoid == F_RI_FKEY_SETDEFAULT_UPD ||
12188 trgform->tgfoid == F_RI_FKEY_NOACTION_DEL ||
12189 trgform->tgfoid == F_RI_FKEY_NOACTION_UPD);
12190
12191 /*
12192 * The constraint is originally set up to contain this trigger as an
12193 * implementation object, so there's a dependency record that links
12194 * the two; however, since the trigger is no longer needed, we remove
12195 * the dependency link in order to be able to drop the trigger while
12196 * keeping the constraint intact.
12197 */
12199 trgform->oid,
12200 false);
12201 /* make dependency deletion visible to performDeletion */
12204 trgform->oid);
12206 /* make trigger drop visible, in case the loop iterates */
12208 }
12209
12210 systable_endscan(scan);
12211}

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

Referenced by ATExecAlterFKConstrEnforceability(), and AttachPartitionForeignKey().

◆ equal_oid_lists()

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

Definition at line 23539 of file tablecmds.c.

23540{
23541 ListCell *la,
23542 *lb;
23543
23544 if (list_length(a) != list_length(b))
23545 return false;
23546
23547 forboth(la, a, lb, b)
23548 {
23549 if (lfirst_oid(la) != lfirst_oid(lb))
23550 return false;
23551 }
23552 return true;
23553}

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

Referenced by collectPartitionIndexExtDeps().

◆ evaluateGeneratedExpressionsAndCheckConstraints()

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

Definition at line 22906 of file tablecmds.c.

22910{
22911 econtext->ecxt_scantuple = insertslot;
22912
22914 {
22915 if (!ex->is_generated)
22916 continue;
22917
22918 insertslot->tts_values[ex->attnum - 1]
22919 = ExecEvalExpr(ex->exprstate,
22920 econtext,
22921 &insertslot->tts_isnull[ex->attnum - 1]);
22922 }
22923
22925 {
22926 switch (con->contype)
22927 {
22928 case CONSTR_CHECK:
22929 if (!ExecCheck(con->qualstate, econtext))
22930 ereport(ERROR,
22932 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
22934 errtableconstraint(newPartRel, con->name));
22935 break;
22936 case CONSTR_NOTNULL:
22937 case CONSTR_FOREIGN:
22938 /* Nothing to do here */
22939 break;
22940 default:
22941 elog(ERROR, "unrecognized constraint type: %d",
22942 (int) con->contype);
22943 }
22944 }
22945}
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 1924 of file tablecmds.c.

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

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

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

7079{
7081 ScanKeyData key[2];
7084
7085 /* since this function recurses, it could be driven to stack overflow */
7087
7088 /*
7089 * We scan pg_depend to find those things that depend on the given type.
7090 * (We assume we can ignore refobjsubid for a type.)
7091 */
7093
7094 ScanKeyInit(&key[0],
7098 ScanKeyInit(&key[1],
7101 ObjectIdGetDatum(typeOid));
7102
7104 NULL, 2, key);
7105
7107 {
7109 Relation rel;
7112
7113 /* Check for directly dependent types */
7114 if (pg_depend->classid == TypeRelationId)
7115 {
7116 /*
7117 * This must be an array, domain, or range containing the given
7118 * type, so recursively check for uses of this type. Note that
7119 * any error message will mention the original type not the
7120 * container; this is intentional.
7121 */
7124 continue;
7125 }
7126
7127 /* Else, ignore dependees that aren't relations */
7128 if (pg_depend->classid != RelationRelationId)
7129 continue;
7130
7133
7134 /*
7135 * If objsubid identifies a specific column, refer to that in error
7136 * messages. Otherwise, search to see if there's a user column of the
7137 * type. (We assume system columns are never of interesting types.)
7138 * The search is needed because an index containing an expression
7139 * column of the target type will just be recorded as a whole-relation
7140 * dependency. If we do not find a column of the type, the dependency
7141 * must indicate that the type is transiently referenced in an index
7142 * expression but not stored on disk, which we assume is OK, just as
7143 * we do for references in views. (It could also be that the target
7144 * type is embedded in some container type that is stored in an index
7145 * column, but the previous recursion should catch such cases.)
7146 */
7147 if (pg_depend->objsubid > 0 && pg_depend->objsubid <= tupleDesc->natts)
7148 att = TupleDescAttr(tupleDesc, pg_depend->objsubid - 1);
7149 else
7150 {
7151 att = NULL;
7152 for (int attno = 1; attno <= tupleDesc->natts; attno++)
7153 {
7154 att = TupleDescAttr(tupleDesc, attno - 1);
7155 if (att->atttypid == typeOid && !att->attisdropped)
7156 break;
7157 att = NULL;
7158 }
7159 if (att == NULL)
7160 {
7161 /* No such column, so assume OK */
7163 continue;
7164 }
7165 }
7166
7167 /*
7168 * We definitely should reject if the relation has storage. If it's
7169 * partitioned, then perhaps we don't have to reject: if there are
7170 * partitions then we'll fail when we find one, else there is no
7171 * stored data to worry about. However, it's possible that the type
7172 * change would affect conclusions about whether the type is sortable
7173 * or hashable and thus (if it's a partitioning column) break the
7174 * partitioning rule. For now, reject for partitioned rels too.
7175 */
7176 if (RELKIND_HAS_STORAGE(rel->rd_rel->relkind) ||
7177 RELKIND_HAS_PARTITIONS(rel->rd_rel->relkind))
7178 {
7179 if (origTypeName)
7180 ereport(ERROR,
7182 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7185 NameStr(att->attname))));
7186 else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7187 ereport(ERROR,
7189 errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it",
7192 NameStr(att->attname))));
7193 else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
7194 ereport(ERROR,
7196 errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type",
7199 NameStr(att->attname))));
7200 else
7201 ereport(ERROR,
7203 errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type",
7206 NameStr(att->attname))));
7207 }
7208 else if (OidIsValid(rel->rd_rel->reltype))
7209 {
7210 /*
7211 * A view or composite type itself isn't a problem, but we must
7212 * recursively check for indirect dependencies via its rowtype.
7213 */
7216 }
7217
7219 }
7220
7222
7224}

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

7236{
7238 ScanKeyData key[1];
7239 TableScanDesc scan;
7240 HeapTuple tuple;
7241 List *result = NIL;
7242
7244
7245 ScanKeyInit(&key[0],
7248 ObjectIdGetDatum(typeOid));
7249
7250 scan = table_beginscan_catalog(classRel, 1, key);
7251
7252 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7253 {
7255
7256 if (behavior == DROP_RESTRICT)
7257 ereport(ERROR,
7259 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7260 typeName),
7261 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7262 else
7264 }
7265
7266 table_endscan(scan);
7268
7269 return result;
7270}

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

3690{
3691 ListCell *lc;
3692 int i = 1;
3693
3694 foreach(lc, columns)
3695 {
3696 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3697 return i;
3698
3699 i++;
3700 }
3701 return 0;
3702}

References fb(), i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 14173 of file tablecmds.c.

14174{
14175 CoercionPathType ret;
14176
14178 {
14180 *funcid = InvalidOid;
14181 }
14182 else
14183 {
14185 COERCION_IMPLICIT, funcid);
14186 if (ret == COERCION_PATH_NONE)
14187 /* A previously-relied-upon cast is now gone. */
14188 elog(ERROR, "could not find cast from %u to %u",
14190 }
14191
14192 return ret;
14193}
CoercionPathType find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, CoercionContext ccontext, Oid *funcid)
@ COERCION_PATH_NONE
@ COERCION_PATH_RELABELTYPE

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

Referenced by ATAddForeignKeyConstraint().

◆ freePartitionIndexExtDeps()

static void freePartitionIndexExtDeps ( List extDepState)
static

Definition at line 23742 of file tablecmds.c.

23743{
23745 {
23746 list_free(entry->extensionOids);
23747 pfree(entry);
23748 }
23750}

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

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ GetAttributeCompression()

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

Definition at line 22790 of file tablecmds.c.

22791{
22792 char cmethod;
22793
22794 if (compression == NULL || strcmp(compression, "default") == 0)
22796
22797 /*
22798 * To specify a nondefault method, the column data type must be toastable.
22799 * Note this says nothing about whether the column's attstorage setting
22800 * permits compression; we intentionally allow attstorage and
22801 * attcompression to be independent. But with a non-toastable type,
22802 * attstorage could not be set to a value that would permit compression.
22803 *
22804 * We don't actually need to enforce this, since nothing bad would happen
22805 * if attcompression were non-default; it would never be consulted. But
22806 * it seems more user-friendly to complain about a certainly-useless
22807 * attempt to set the property.
22808 */
22809 if (!TypeIsToastable(atttypid))
22810 ereport(ERROR,
22812 errmsg("column data type %s does not support compression",
22813 format_type_be(atttypid))));
22814
22815 cmethod = CompressionNameToMethod(compression);
22817 ereport(ERROR,
22819 errmsg("invalid compression method \"%s\"", compression)));
22820
22821 return cmethod;
22822}
#define TypeIsToastable(typid)
Definition lsyscache.h:227
char CompressionNameToMethod(const char *compression)
#define CompressionMethodIsValid(cm)

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

Referenced by ATExecSetCompression(), and BuildDescForRelation().

◆ getAttributesList()

static List * getAttributesList ( Relation  parent_rel)
static

Definition at line 22951 of file tablecmds.c.

22952{
22955 List *colList = NIL;
22956
22958
22959 for (parent_attno = 1; parent_attno <= modelDesc->natts;
22960 parent_attno++)
22961 {
22963 parent_attno - 1);
22964 ColumnDef *def;
22965
22966 /* Ignore dropped columns in the parent. */
22967 if (attribute->attisdropped)
22968 continue;
22969
22970 def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
22971 attribute->atttypmod, attribute->attcollation);
22972
22973 def->is_not_null = attribute->attnotnull;
22974
22975 /* Copy identity. */
22976 def->identity = attribute->attidentity;
22977
22978 /* Copy attgenerated. */
22979 def->generated = attribute->attgenerated;
22980
22981 def->storage = attribute->attstorage;
22982
22983 /* Likewise, copy compression. */
22984 if (CompressionMethodIsValid(attribute->attcompression))
22985 def->compression =
22986 pstrdup(GetCompressionMethodName(attribute->attcompression));
22987 else
22988 def->compression = NULL;
22989
22990 /* Add to column list. */
22991 colList = lappend(colList, def);
22992 }
22993
22994 return colList;
22995}
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 22828 of file tablecmds.c.

22829{
22830 char cstorage = 0;
22831
22832 if (pg_strcasecmp(storagemode, "plain") == 0)
22834 else if (pg_strcasecmp(storagemode, "external") == 0)
22836 else if (pg_strcasecmp(storagemode, "extended") == 0)
22838 else if (pg_strcasecmp(storagemode, "main") == 0)
22840 else if (pg_strcasecmp(storagemode, "default") == 0)
22841 cstorage = get_typstorage(atttypid);
22842 else
22843 ereport(ERROR,
22845 errmsg("invalid storage type \"%s\"",
22846 storagemode)));
22847
22848 /*
22849 * safety check: do not allow toasted storage modes unless column datatype
22850 * is TOAST-aware.
22851 */
22852 if (!(cstorage == TYPSTORAGE_PLAIN || TypeIsToastable(atttypid)))
22853 ereport(ERROR,
22855 errmsg("column data type %s can only have storage PLAIN",
22856 format_type_be(atttypid))));
22857
22858 return cstorage;
22859}
char get_typstorage(Oid typid)
Definition lsyscache.c:2735
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 12219 of file tablecmds.c.

12223{
12225 SysScanDesc scan;
12227
12229 ScanKeyInit(&key,
12232 ObjectIdGetDatum(conoid));
12233
12235 NULL, 1, &key);
12236 while ((trigtup = systable_getnext(scan)) != NULL)
12237 {
12239
12240 if (trgform->tgconstrrelid != conrelid)
12241 continue;
12242 if (trgform->tgrelid != confrelid)
12243 continue;
12244 /* Only ever look at "action" triggers on the PK side. */
12246 continue;
12247 if (TRIGGER_FOR_DELETE(trgform->tgtype))
12248 {
12250 *deleteTriggerOid = trgform->oid;
12251 }
12252 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12253 {
12255 *updateTriggerOid = trgform->oid;
12256 }
12257#ifndef USE_ASSERT_CHECKING
12258 /* In an assert-enabled build, continue looking to find duplicates */
12260 break;
12261#endif
12262 }
12263
12265 elog(ERROR, "could not find ON DELETE action trigger of foreign key constraint %u",
12266 conoid);
12268 elog(ERROR, "could not find ON UPDATE action trigger of foreign key constraint %u",
12269 conoid);
12270
12271 systable_endscan(scan);
12272}
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 12280 of file tablecmds.c.

12284{
12286 SysScanDesc scan;
12288
12290 ScanKeyInit(&key,
12293 ObjectIdGetDatum(conoid));
12294
12296 NULL, 1, &key);
12297 while ((trigtup = systable_getnext(scan)) != NULL)
12298 {
12300
12301 if (trgform->tgconstrrelid != confrelid)
12302 continue;
12303 if (trgform->tgrelid != conrelid)
12304 continue;
12305 /* Only ever look at "check" triggers on the FK side. */
12307 continue;
12308 if (TRIGGER_FOR_INSERT(trgform->tgtype))
12309 {
12311 *insertTriggerOid = trgform->oid;
12312 }
12313 else if (TRIGGER_FOR_UPDATE(trgform->tgtype))
12314 {
12316 *updateTriggerOid = trgform->oid;
12317 }
12318#ifndef USE_ASSERT_CHECKING
12319 /* In an assert-enabled build, continue looking to find duplicates. */
12321 break;
12322#endif
12323 }
12324
12326 elog(ERROR, "could not find ON INSERT check triggers of foreign key constraint %u",
12327 conoid);
12329 elog(ERROR, "could not find ON UPDATE check triggers of foreign key constraint %u",
12330 conoid);
12331
12332 systable_endscan(scan);
12333}
#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 22688 of file tablecmds.c.

22689{
22691 HeapTuple tuple;
22692 SysScanDesc scan;
22693 ScanKeyData key[2];
22694 List *constraints = NIL;
22695
22696 /*
22697 * If no indexes, or no columns are referenceable by FKs, we can avoid the
22698 * scan.
22699 */
22703 return NIL;
22704
22705 /* Search for constraints referencing this table */
22707 ScanKeyInit(&key[0],
22710 ScanKeyInit(&key[1],
22713
22714 /* XXX This is a seqscan, as we don't have a usable index */
22715 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
22716 while ((tuple = systable_getnext(scan)) != NULL)
22717 {
22719
22720 /*
22721 * We only need to process constraints that are part of larger ones.
22722 */
22723 if (!OidIsValid(constrForm->conparentid))
22724 continue;
22725
22726 constraints = lappend_oid(constraints, constrForm->oid);
22727 }
22728
22729 systable_endscan(scan);
22731
22732 return constraints;
22733}
#define bms_is_empty(a)
Definition bitmapset.h:119
@ INDEX_ATTR_BITMAP_KEY
Definition relcache.h:70

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

Referenced by ATDetachCheckNoForeignKeyRefs(), and DetachPartitionFinalize().

◆ index_copy_data()

static void index_copy_data ( Relation  rel,
RelFileLocator  newrlocator 
)
static

Definition at line 17859 of file tablecmds.c.

17860{
17862
17863 /*
17864 * Since we copy the file directly without looking at the shared buffers,
17865 * we'd better first flush out any pages of the source relation that are
17866 * in shared buffers. We assume no new changes will be made while we are
17867 * holding exclusive lock on the rel.
17868 */
17870
17871 /*
17872 * Create and copy all forks of the relation, and schedule unlinking of
17873 * old physical files.
17874 *
17875 * NOTE: any conflict in relfilenumber value will be caught in
17876 * RelationCreateStorage().
17877 */
17878 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17879
17880 /* copy main fork */
17882 rel->rd_rel->relpersistence);
17883
17884 /* copy those extra forks that exist */
17885 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17886 forkNum <= MAX_FORKNUM; forkNum++)
17887 {
17888 if (smgrexists(RelationGetSmgr(rel), forkNum))
17889 {
17890 smgrcreate(dstrel, forkNum, false);
17891
17892 /*
17893 * WAL log creation if the relation is persistent, or this is the
17894 * init fork of an unlogged relation.
17895 */
17896 if (RelationIsPermanent(rel) ||
17897 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17898 forkNum == INIT_FORKNUM))
17899 log_smgrcreate(&newrlocator, forkNum);
17901 rel->rd_rel->relpersistence);
17902 }
17903 }
17904
17905 /* drop old relation, and close new one */
17908}
void FlushRelationBuffers(Relation rel)
Definition bufmgr.c:5185
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 18570 of file tablecmds.c.

18571{
18573 SysScanDesc scan;
18576 bool found = false;
18577
18578 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
18579
18580 /*
18581 * Find pg_inherits entries by inhparent. (We need to scan them all in
18582 * order to verify that no other partition is pending detach.)
18583 */
18585 ScanKeyInit(&key,
18590 true, NULL, 1, &key);
18591
18593 {
18595
18597 if (inhForm->inhdetachpending)
18598 ereport(ERROR,
18600 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
18601 get_rel_name(inhForm->inhrelid),
18602 get_namespace_name(parent_rel->rd_rel->relnamespace),
18604 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
18605
18606 if (inhForm->inhrelid == RelationGetRelid(child_rel))
18607 {
18609
18612
18614 &inheritsTuple->t_self,
18615 newtup);
18616 found = true;
18618 /* keep looking, to ensure we catch others pending detach */
18619 }
18620 }
18621
18622 /* Done */
18623 systable_endscan(scan);
18625
18626 if (!found)
18627 ereport(ERROR,
18629 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18632}
#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 2614 of file tablecmds.c.

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

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

18209{
18212
18215
18217 {
18219 char *parent_attname = NameStr(parent_att->attname);
18220 HeapTuple tuple;
18221
18222 /* Ignore dropped columns in the parent. */
18223 if (parent_att->attisdropped)
18224 continue;
18225
18226 /* Find same column in child (matching on column name). */
18228 if (HeapTupleIsValid(tuple))
18229 {
18231
18232 if (parent_att->atttypid != child_att->atttypid ||
18233 parent_att->atttypmod != child_att->atttypmod)
18234 ereport(ERROR,
18236 errmsg("child table \"%s\" has different type for column \"%s\"",
18238
18239 if (parent_att->attcollation != child_att->attcollation)
18240 ereport(ERROR,
18242 errmsg("child table \"%s\" has different collation for column \"%s\"",
18244
18245 /*
18246 * If the parent has a not-null constraint that's not NO INHERIT,
18247 * make sure the child has one too.
18248 *
18249 * Other constraints are checked elsewhere.
18250 */
18251 if (parent_att->attnotnull && !child_att->attnotnull)
18252 {
18254
18256 parent_att->attnum);
18257 if (HeapTupleIsValid(contup) &&
18259 ereport(ERROR,
18261 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
18263 }
18264
18265 /*
18266 * Child column must be generated if and only if parent column is.
18267 */
18268 if (parent_att->attgenerated && !child_att->attgenerated)
18269 ereport(ERROR,
18271 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
18272 if (child_att->attgenerated && !parent_att->attgenerated)
18273 ereport(ERROR,
18275 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
18276
18277 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
18278 ereport(ERROR,
18280 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
18281 errdetail("Parent column is %s, child column is %s.",
18282 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
18283 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
18284
18285 /*
18286 * Regular inheritance children are independent enough not to
18287 * inherit identity columns. But partitions are integral part of
18288 * a partitioned table and inherit identity column.
18289 */
18290 if (ispartition)
18291 child_att->attidentity = parent_att->attidentity;
18292
18293 /*
18294 * OK, bump the child column's inheritance count. (If we fail
18295 * later on, this change will just roll back.)
18296 */
18297 if (pg_add_s16_overflow(child_att->attinhcount, 1,
18298 &child_att->attinhcount))
18299 ereport(ERROR,
18301 errmsg("too many inheritance parents"));
18302
18303 /*
18304 * In case of partitions, we must enforce that value of attislocal
18305 * is same in all partitions. (Note: there are only inherited
18306 * attributes in partitions)
18307 */
18308 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18309 {
18310 Assert(child_att->attinhcount == 1);
18311 child_att->attislocal = false;
18312 }
18313
18314 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
18315 heap_freetuple(tuple);
18316 }
18317 else
18318 {
18319 ereport(ERROR,
18321 errmsg("child table is missing column \"%s\"", parent_attname)));
18322 }
18323 }
18324
18326}

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

3247{
3248 ListCell *lc;
3250
3251 foreach(lc, constraints)
3252 {
3254
3255 Assert(ccon->contype == CONSTR_CHECK);
3256
3257 /* Non-matching names never conflict */
3258 if (strcmp(ccon->name, name) != 0)
3259 continue;
3260
3261 if (equal(expr, ccon->expr))
3262 {
3263 /* OK to merge constraint with existing */
3264 if (pg_add_s16_overflow(ccon->inhcount, 1,
3265 &ccon->inhcount))
3266 ereport(ERROR,
3268 errmsg("too many inheritance parents"));
3269
3270 /*
3271 * When enforceability differs, the merged constraint should be
3272 * marked as ENFORCED because one of the parents is ENFORCED.
3273 */
3274 if (!ccon->is_enforced && is_enforced)
3275 {
3276 ccon->is_enforced = true;
3277 ccon->skip_validation = false;
3278 }
3279
3280 return constraints;
3281 }
3282
3283 ereport(ERROR,
3285 errmsg("check constraint name \"%s\" appears multiple times but with different expressions",
3286 name)));
3287 }
3288
3289 /*
3290 * Constraint couldn't be merged with an existing one and also didn't
3291 * conflict with an existing one, so add it as a new one to the list.
3292 */
3294 newcon->contype = CONSTR_CHECK;
3295 newcon->name = pstrdup(name);
3296 newcon->expr = expr;
3297 newcon->inhcount = 1;
3298 newcon->is_enforced = is_enforced;
3299 newcon->skip_validation = !is_enforced;
3300 return lappend(constraints, newcon);
3301}

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

3326{
3327 char *attributeName = newdef->colname;
3329 Oid inhtypeid,
3330 newtypeid;
3332 newtypmod;
3333 Oid inhcollid,
3334 newcollid;
3335
3338 (errmsg("merging column \"%s\" with inherited definition",
3339 attributeName)));
3340 else
3342 (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
3343 errdetail("User-specified column moved to the position of the inherited column.")));
3344
3346
3347 /*
3348 * Must have the same type and typmod
3349 */
3353 ereport(ERROR,
3355 errmsg("column \"%s\" has a type conflict",
3357 errdetail("%s versus %s",
3360
3361 /*
3362 * Must have the same collation
3363 */
3366 if (inhcollid != newcollid)
3367 ereport(ERROR,
3369 errmsg("column \"%s\" has a collation conflict",
3371 errdetail("\"%s\" versus \"%s\"",
3374
3375 /*
3376 * Identity is never inherited by a regular inheritance child. Pick
3377 * child's identity definition if there's one.
3378 */
3379 inhdef->identity = newdef->identity;
3380
3381 /*
3382 * Copy storage parameter
3383 */
3384 if (inhdef->storage == 0)
3385 inhdef->storage = newdef->storage;
3386 else if (newdef->storage != 0 && inhdef->storage != newdef->storage)
3387 ereport(ERROR,
3389 errmsg("column \"%s\" has a storage parameter conflict",
3391 errdetail("%s versus %s",
3392 storage_name(inhdef->storage),
3393 storage_name(newdef->storage))));
3394
3395 /*
3396 * Copy compression parameter
3397 */
3398 if (inhdef->compression == NULL)
3399 inhdef->compression = newdef->compression;
3400 else if (newdef->compression != NULL)
3401 {
3402 if (strcmp(inhdef->compression, newdef->compression) != 0)
3403 ereport(ERROR,
3405 errmsg("column \"%s\" has a compression method conflict",
3407 errdetail("%s versus %s", inhdef->compression, newdef->compression)));
3408 }
3409
3410 /*
3411 * Merge of not-null constraints = OR 'em together
3412 */
3413 inhdef->is_not_null |= newdef->is_not_null;
3414
3415 /*
3416 * Check for conflicts related to generated columns.
3417 *
3418 * If the parent column is generated, the child column will be made a
3419 * generated column if it isn't already. If it is a generated column,
3420 * we'll take its generation expression in preference to the parent's. We
3421 * must check that the child column doesn't specify a default value or
3422 * identity, which matches the rules for a single column in
3423 * parse_utilcmd.c.
3424 *
3425 * Conversely, if the parent column is not generated, the child column
3426 * can't be either. (We used to allow that, but it results in being able
3427 * to override the generation expression via UPDATEs through the parent.)
3428 */
3429 if (inhdef->generated)
3430 {
3431 if (newdef->raw_default && !newdef->generated)
3432 ereport(ERROR,
3434 errmsg("column \"%s\" inherits from generated column but specifies default",
3435 inhdef->colname)));
3436 if (newdef->identity)
3437 ereport(ERROR,
3439 errmsg("column \"%s\" inherits from generated column but specifies identity",
3440 inhdef->colname)));
3441 }
3442 else
3443 {
3444 if (newdef->generated)
3445 ereport(ERROR,
3447 errmsg("child column \"%s\" specifies generation expression",
3448 inhdef->colname),
3449 errhint("A child table column cannot be generated unless its parent column is.")));
3450 }
3451
3452 if (inhdef->generated && newdef->generated && newdef->generated != inhdef->generated)
3453 ereport(ERROR,
3455 errmsg("column \"%s\" inherits from generated column of different kind",
3456 inhdef->colname),
3457 errdetail("Parent column is %s, child column is %s.",
3458 inhdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
3459 newdef->generated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
3460
3461 /*
3462 * If new def has a default, override previous default
3463 */
3464 if (newdef->raw_default != NULL)
3465 {
3466 inhdef->raw_default = newdef->raw_default;
3467 inhdef->cooked_default = newdef->cooked_default;
3468 }
3469
3470 /* Mark the column as locally defined */
3471 inhdef->is_local = true;
3472}
static const char * storage_name(char c)
Definition tablecmds.c:2529

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

18347{
18352 Oid parent_relid = RelationGetRelid(parent_rel);
18353 AttrMap *attmap;
18354
18356
18357 /* Outer loop scans through the parent's constraint definitions */
18361 ObjectIdGetDatum(parent_relid));
18363 true, NULL, 1, &parent_key);
18364
18367 true);
18368
18370 {
18376 bool found = false;
18377
18378 if (parent_con->contype != CONSTRAINT_CHECK &&
18379 parent_con->contype != CONSTRAINT_NOTNULL)
18380 continue;
18381
18382 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
18383 if (parent_con->connoinherit)
18384 continue;
18385
18386 if (parent_con->contype == CONSTRAINT_NOTNULL)
18388 else
18390
18391 /* Search for a child constraint matching this one */
18397 true, NULL, 1, &child_key);
18398
18400 {
18403
18404 if (child_con->contype != parent_con->contype)
18405 continue;
18406
18407 /*
18408 * CHECK constraint are matched by constraint name, NOT NULL ones
18409 * by attribute number.
18410 */
18411 if (child_con->contype == CONSTRAINT_CHECK)
18412 {
18413 if (strcmp(NameStr(parent_con->conname),
18414 NameStr(child_con->conname)) != 0)
18415 continue;
18416 }
18417 else if (child_con->contype == CONSTRAINT_NOTNULL)
18418 {
18422
18425 if (parent_attno != attmap->attnums[child_attno - 1])
18426 continue;
18427
18429 /* there shouldn't be constraints on dropped columns */
18430 if (parent_attr->attisdropped || child_attr->attisdropped)
18431 elog(ERROR, "found not-null constraint on dropped columns");
18432 }
18433
18434 if (child_con->contype == CONSTRAINT_CHECK &&
18436 ereport(ERROR,
18438 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
18440
18441 /*
18442 * If the child constraint is "no inherit" then cannot merge
18443 */
18444 if (child_con->connoinherit)
18445 ereport(ERROR,
18447 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
18449
18450 /*
18451 * If the child constraint is "not valid" then cannot merge with a
18452 * valid parent constraint
18453 */
18454 if (parent_con->convalidated && child_con->conenforced &&
18455 !child_con->convalidated)
18456 ereport(ERROR,
18458 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
18460
18461 /*
18462 * A NOT ENFORCED child constraint cannot be merged with an
18463 * ENFORCED parent constraint. However, the reverse is allowed,
18464 * where the child constraint is ENFORCED.
18465 */
18466 if (parent_con->conenforced && !child_con->conenforced)
18467 ereport(ERROR,
18469 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
18471
18472 /*
18473 * OK, bump the child constraint's inheritance count. (If we fail
18474 * later on, this change will just roll back.)
18475 */
18478
18479 if (pg_add_s16_overflow(child_con->coninhcount, 1,
18480 &child_con->coninhcount))
18481 ereport(ERROR,
18483 errmsg("too many inheritance parents"));
18484
18485 /*
18486 * In case of partitions, an inherited constraint must be
18487 * inherited only once since it cannot have multiple parents and
18488 * it is never considered local.
18489 */
18490 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18491 {
18492 Assert(child_con->coninhcount == 1);
18493 child_con->conislocal = false;
18494 }
18495
18498
18499 found = true;
18500 break;
18501 }
18502
18504
18505 if (!found)
18506 {
18507 if (parent_con->contype == CONSTRAINT_NOTNULL)
18508 ereport(ERROR,
18510 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
18511 get_attname(parent_relid,
18513 false),
18515
18516 ereport(ERROR,
18518 errmsg("child table is missing constraint \"%s\"",
18519 NameStr(parent_con->conname))));
18520 }
18521 }
18522
18525}

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

3498{
3499 char *attributeName = newdef->colname;
3502 newtypeid;
3504 newtypmod;
3506 newcollid;
3507
3509 (errmsg("merging multiple inherited definitions of column \"%s\"",
3510 attributeName)));
3512
3513 /*
3514 * Must have the same type and typmod
3515 */
3519 ereport(ERROR,
3521 errmsg("inherited column \"%s\" has a type conflict",
3523 errdetail("%s versus %s",
3526
3527 /*
3528 * Must have the same collation
3529 */
3532 if (prevcollid != newcollid)
3533 ereport(ERROR,
3535 errmsg("inherited column \"%s\" has a collation conflict",
3537 errdetail("\"%s\" versus \"%s\"",
3540
3541 /*
3542 * Copy/check storage parameter
3543 */
3544 if (prevdef->storage == 0)
3545 prevdef->storage = newdef->storage;
3546 else if (prevdef->storage != newdef->storage)
3547 ereport(ERROR,
3549 errmsg("inherited column \"%s\" has a storage parameter conflict",
3551 errdetail("%s versus %s",
3552 storage_name(prevdef->storage),
3553 storage_name(newdef->storage))));
3554
3555 /*
3556 * Copy/check compression parameter
3557 */
3558 if (prevdef->compression == NULL)
3559 prevdef->compression = newdef->compression;
3560 else if (newdef->compression != NULL)
3561 {
3562 if (strcmp(prevdef->compression, newdef->compression) != 0)
3563 ereport(ERROR,
3565 errmsg("column \"%s\" has a compression method conflict",
3567 errdetail("%s versus %s",
3568 prevdef->compression, newdef->compression)));
3569 }
3570
3571 /*
3572 * Check for GENERATED conflicts
3573 */
3574 if (prevdef->generated != newdef->generated)
3575 ereport(ERROR,
3577 errmsg("inherited column \"%s\" has a generation conflict",
3578 attributeName)));
3579
3580 /*
3581 * Default and other constraints are handled by the caller.
3582 */
3583
3584 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3585 &prevdef->inhcount))
3586 ereport(ERROR,
3588 errmsg("too many inheritance parents"));
3589
3590 return prevdef;
3591}

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

23363{
23364 CommandId mycid;
23365 EState *estate;
23366 AlteredTableInfo *tab;
23367 ListCell *ltab;
23368
23369 /* The FSM is empty, so don't bother using it. */
23370 uint32 ti_options = TABLE_INSERT_SKIP_FSM;
23371 BulkInsertState bistate; /* state of bulk inserts for partition */
23372 TupleTableSlot *dstslot;
23373
23374 /* Find the work queue entry for the new partition table: newPartRel. */
23376
23377 /* Generate the constraint and default execution states. */
23378 estate = CreateExecutorState();
23379
23381
23382 mycid = GetCurrentCommandId(true);
23383
23384 /* Prepare a BulkInsertState for table_tuple_insert. */
23385 bistate = GetBulkInsertState();
23386
23387 /* Create the necessary tuple slot. */
23388 dstslot = table_slot_create(newPartRel, NULL);
23389
23391 {
23392 ExprContext *econtext;
23395 TableScanDesc scan;
23397 Snapshot snapshot;
23399
23400 econtext = GetPerTupleExprContext(estate);
23401
23402 /*
23403 * Partition is already locked in the transformPartitionCmdForMerge
23404 * function.
23405 */
23407
23408 /* Create a source tuple slot for the partition being merged. */
23410
23411 /*
23412 * Map computing for moving attributes of the merged partition to the
23413 * new partition.
23414 */
23417
23418 /* Scan through the rows. */
23419 snapshot = RegisterSnapshot(GetLatestSnapshot());
23420 scan = table_beginscan(mergingPartition, snapshot, 0, NULL,
23421 SO_NONE);
23422
23423 /*
23424 * Switch to per-tuple memory context and reset it for each tuple
23425 * produced, so we don't leak memory.
23426 */
23428
23430 {
23432
23434
23435 if (tuple_map)
23436 {
23437 /* Need to use a map to copy attributes. */
23438 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
23439 }
23440 else
23441 {
23443
23444 /* Copy attributes directly. */
23445 insertslot = dstslot;
23446
23448
23449 memcpy(insertslot->tts_values, srcslot->tts_values,
23450 sizeof(Datum) * srcslot->tts_nvalid);
23451 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23452 sizeof(bool) * srcslot->tts_nvalid);
23453
23455 }
23456
23457 /*
23458 * Constraints and GENERATED expressions might reference the
23459 * tableoid column, so fill tts_tableOid with the desired value.
23460 * (We must do this each time, because it gets overwritten with
23461 * newrel's OID during storing.)
23462 */
23463 insertslot->tts_tableOid = RelationGetRelid(newPartRel);
23464
23465 /*
23466 * Now, evaluate any generated expressions whose inputs come from
23467 * the new tuple. We assume these columns won't reference each
23468 * other, so that there's no ordering dependency.
23469 */
23471 insertslot, econtext);
23472
23473 /* Write the tuple out to the new relation. */
23475 ti_options, bistate);
23476
23477 ResetExprContext(econtext);
23478 }
23479
23481 table_endscan(scan);
23482 UnregisterSnapshot(snapshot);
23483
23484 if (tuple_map)
23486
23489 }
23490
23491 FreeExecutorState(estate);
23493 FreeBulkInsertState(bistate);
23494
23496
23497 /*
23498 * We don't need to process this newPartRel since we already processed it
23499 * here, so delete the ALTER TABLE queue for it.
23500 */
23501 foreach(ltab, *wqueue)
23502 {
23503 tab = (AlteredTableInfo *) lfirst(ltab);
23504 if (tab->relid == RelationGetRelid(newPartRel))
23505 {
23507 break;
23508 }
23509 }
23510}
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 8230 of file tablecmds.c.

8231{
8233
8234 nnulltest->arg = (Expr *) makeVar(1,
8235 attr->attnum,
8236 attr->atttypid,
8237 attr->atttypmod,
8238 attr->attcollation,
8239 0);
8240 nnulltest->nulltesttype = IS_NOT_NULL;
8241
8242 /*
8243 * argisrow = false is correct even for a composite column, because
8244 * attnotnull does not represent a SQL-spec IS NOT NULL test in such a
8245 * case, just IS DISTINCT FROM NULL.
8246 */
8247 nnulltest->argisrow = false;
8248 nnulltest->location = -1;
8249
8251 {
8253 (errmsg_internal("existing constraints on column \"%s.%s\" are sufficient to prove that it does not contain nulls",
8254 RelationGetRelationName(rel), NameStr(attr->attname))));
8255 return true;
8256 }
8257
8258 return false;
8259}
@ IS_NOT_NULL
Definition primnodes.h:1975
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 20791 of file tablecmds.c.

20793{
20795 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20796 int i;
20797
20798 if (constr && constr->has_not_null)
20799 {
20800 int natts = scanrel->rd_att->natts;
20801
20802 for (i = 1; i <= natts; i++)
20803 {
20804 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20805
20806 /* invalid not-null constraint must be ignored here */
20807 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20808 {
20811
20812 ntest->arg = (Expr *) makeVar(1,
20813 i,
20814 wholeatt->atttypid,
20815 wholeatt->atttypmod,
20816 wholeatt->attcollation,
20817 0);
20818 ntest->nulltesttype = IS_NOT_NULL;
20819
20820 /*
20821 * argisrow=false is correct even for a composite column,
20822 * because attnotnull does not represent a SQL-spec IS NOT
20823 * NULL test in such a case, just IS DISTINCT FROM NULL.
20824 */
20825 ntest->argisrow = false;
20826 ntest->location = -1;
20828 }
20829 }
20830 }
20831
20833}

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

20024{
20025 ListCell *l;
20028
20029 foreach(l, on_commits)
20030 {
20032
20033 /* Ignore entry if already dropped in this xact */
20034 if (oc->deleting_subid != InvalidSubTransactionId)
20035 continue;
20036
20037 switch (oc->oncommit)
20038 {
20039 case ONCOMMIT_NOOP:
20041 /* Do nothing (there shouldn't be such entries, actually) */
20042 break;
20044
20045 /*
20046 * If this transaction hasn't accessed any temporary
20047 * relations, we can skip truncating ON COMMIT DELETE ROWS
20048 * tables, as they must still be empty.
20049 */
20052 break;
20053 case ONCOMMIT_DROP:
20055 break;
20056 }
20057 }
20058
20059 /*
20060 * Truncate relations before dropping so that all dependencies between
20061 * relations are removed after they are worked on. Doing it like this
20062 * might be a waste as it is possible that a relation being truncated will
20063 * be dropped anyway due to its parent being dropped, but this makes the
20064 * code more robust because of not having to re-check that the relation
20065 * exists at truncation time.
20066 */
20067 if (oids_to_truncate != NIL)
20069
20070 if (oids_to_drop != NIL)
20071 {
20073
20074 foreach(l, oids_to_drop)
20075 {
20076 ObjectAddress object;
20077
20078 object.classId = RelationRelationId;
20079 object.objectId = lfirst_oid(l);
20080 object.objectSubId = 0;
20081
20083
20085 }
20086
20087 /*
20088 * Object deletion might involve toast table access (to clean up
20089 * toasted catalog entries), so ensure we have a valid snapshot.
20090 */
20092
20093 /*
20094 * Since this is an automatic drop, rather than one directly initiated
20095 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
20096 */
20099
20101
20102#ifdef USE_ASSERT_CHECKING
20103
20104 /*
20105 * Note that table deletion will call remove_on_commit_action, so the
20106 * entry should get marked as deleted.
20107 */
20108 foreach(l, on_commits)
20109 {
20111
20112 if (oc->oncommit != ONCOMMIT_DROP)
20113 continue;
20114
20115 Assert(oc->deleting_subid != InvalidSubTransactionId);
20116 }
20117#endif
20118 }
20119}
#define PERFORM_DELETION_QUIETLY
Definition dependency.h:94
void heap_truncate(List *relids)
Definition heap.c:3611
@ ONCOMMIT_DELETE_ROWS
Definition primnodes.h:61
@ ONCOMMIT_PRESERVE_ROWS
Definition primnodes.h:60
@ ONCOMMIT_DROP
Definition primnodes.h:62
int MyXactFlags
Definition xact.c:138
#define XACT_FLAGS_ACCESSEDTEMPNAMESPACE
Definition xact.h:103

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

Referenced by CommitTransaction(), and PrepareTransaction().

◆ QueueCheckConstraintValidation()

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

Definition at line 13654 of file tablecmds.c.

13657{
13659 AlteredTableInfo *tab;
13662
13663 List *children = NIL;
13664 ListCell *child;
13666 Datum val;
13667 char *conbin;
13668
13670 Assert(con->contype == CONSTRAINT_CHECK);
13671
13672 /*
13673 * If we're recursing, the parent has already done this, so skip it. Also,
13674 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13675 * for it in the children.
13676 */
13677 if (!recursing && !con->connoinherit)
13678 children = find_all_inheritors(RelationGetRelid(rel),
13679 lockmode, NULL);
13680
13681 /*
13682 * For CHECK constraints, we must ensure that we only mark the constraint
13683 * as validated on the parent if it's already validated on the children.
13684 *
13685 * We recurse before validating on the parent, to reduce risk of
13686 * deadlocks.
13687 */
13688 foreach(child, children)
13689 {
13690 Oid childoid = lfirst_oid(child);
13692
13693 if (childoid == RelationGetRelid(rel))
13694 continue;
13695
13696 /*
13697 * If we are told not to recurse, there had better not be any child
13698 * tables, because we can't mark the constraint on the parent valid
13699 * unless it is valid for all child tables.
13700 */
13701 if (!recurse)
13702 ereport(ERROR,
13704 errmsg("constraint must be validated on child tables too")));
13705
13706 /* find_all_inheritors already got lock */
13708
13710 true, lockmode);
13712 }
13713
13714 /* Queue validation for phase 3 */
13716 newcon->name = constrName;
13717 newcon->contype = CONSTR_CHECK;
13718 newcon->refrelid = InvalidOid;
13719 newcon->refindid = InvalidOid;
13720 newcon->conid = con->oid;
13721
13726
13727 /* Find or create work queue entry for this table */
13728 tab = ATGetQueueEntry(wqueue, rel);
13729 tab->constraints = lappend(tab->constraints, newcon);
13730
13731 /*
13732 * Invalidate relcache so that others see the new validated constraint.
13733 */
13735
13736 /*
13737 * Now update the catalog, while we have the door open.
13738 */
13741 copy_con->convalidated = true;
13743
13745
13747}

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

13534{
13536 AlteredTableInfo *tab;
13539
13540 /* since this function recurses, it could be driven to stack overflow */
13542
13544 Assert(con->contype == CONSTRAINT_FOREIGN);
13545 Assert(!con->convalidated);
13546
13547 /*
13548 * Add the validation to phase 3's queue; not needed for partitioned
13549 * tables themselves, only for their partitions.
13550 *
13551 * When the referenced table (pkrelid) is partitioned, the referencing
13552 * table (fkrel) has one pg_constraint row pointing to each partition
13553 * thereof. These rows are there only to support action triggers and no
13554 * table scan is needed, therefore skip this for them as well.
13555 */
13556 if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13557 con->confrelid == pkrelid)
13558 {
13561
13562 /* Queue validation for phase 3 */
13564 /* for now this is all we need */
13565 fkconstraint->conname = pstrdup(NameStr(con->conname));
13566
13568 newcon->name = fkconstraint->conname;
13569 newcon->contype = CONSTR_FOREIGN;
13570 newcon->refrelid = con->confrelid;
13571 newcon->refindid = con->conindid;
13572 newcon->conid = con->oid;
13573 newcon->qual = (Node *) fkconstraint;
13574
13575 /* Find or create work queue entry for this table */
13577 tab->constraints = lappend(tab->constraints, newcon);
13578 }
13579
13580 /*
13581 * If the table at either end of the constraint is partitioned, we need to
13582 * recurse and handle every unvalidated constraint that is a child of this
13583 * constraint.
13584 */
13585 if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13586 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13587 {
13588 ScanKeyData pkey;
13591
13592 ScanKeyInit(&pkey,
13595 ObjectIdGetDatum(con->oid));
13596
13598 true, NULL, 1, &pkey);
13599
13601 {
13604
13606
13607 /*
13608 * If the child constraint has already been validated, no further
13609 * action is required for it or its descendants, as they are all
13610 * valid.
13611 */
13612 if (childcon->convalidated)
13613 continue;
13614
13615 childrel = table_open(childcon->conrelid, lockmode);
13616
13617 /*
13618 * NB: Note that pkrelid should be passed as-is during recursion,
13619 * as it is required to identify the root referenced table.
13620 */
13622 childtup, lockmode);
13624 }
13625
13627 }
13628
13629 /*
13630 * Now mark the pg_constraint row as validated (even if we didn't check,
13631 * notably the ones for partitions on the referenced side).
13632 *
13633 * We rely on transaction abort to roll back this change if phase 3
13634 * ultimately finds violating rows. This is a bit ugly.
13635 */
13638 copy_con->convalidated = true;
13640
13642
13644}

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

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

◆ QueueNNConstraintValidation()

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

Definition at line 13757 of file tablecmds.c.

13760{
13762 AlteredTableInfo *tab;
13765 List *children = NIL;
13767 char *colname;
13768
13770 Assert(con->contype == CONSTRAINT_NOTNULL);
13771
13773
13774 /*
13775 * If we're recursing, we've already done this for parent, so skip it.
13776 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13777 * look for it in the children.
13778 *
13779 * We recurse before validating on the parent, to reduce risk of
13780 * deadlocks.
13781 */
13782 if (!recursing && !con->connoinherit)
13783 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13784
13785 colname = get_attname(RelationGetRelid(rel), attnum, false);
13786 foreach_oid(childoid, children)
13787 {
13791 char *conname;
13792
13793 if (childoid == RelationGetRelid(rel))
13794 continue;
13795
13796 /*
13797 * If we are told not to recurse, there had better not be any child
13798 * tables, because we can't mark the constraint on the parent valid
13799 * unless it is valid for all child tables.
13800 */
13801 if (!recurse)
13802 ereport(ERROR,
13804 errmsg("constraint must be validated on child tables too"));
13805
13806 /*
13807 * The column on child might have a different attnum, so search by
13808 * column name.
13809 */
13811 if (!contup)
13812 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13813 colname, get_rel_name(childoid));
13815 if (childcon->convalidated)
13816 continue;
13817
13818 /* find_all_inheritors already got lock */
13820 conname = pstrdup(NameStr(childcon->conname));
13821
13822 /* XXX improve ATExecValidateConstraint API to avoid double search */
13824 false, true, lockmode);
13826 }
13827
13828 /* Set attnotnull appropriately without queueing another validation */
13829 set_attnotnull(NULL, rel, attnum, true, false);
13830
13831 tab = ATGetQueueEntry(wqueue, rel);
13832 tab->verify_new_notnull = true;
13833
13834 /*
13835 * Invalidate relcache so that others see the new validated constraint.
13836 */
13838
13839 /*
13840 * Now update the catalogs, while we have the door open.
13841 */
13844 copy_con->convalidated = true;
13846
13848
13850}

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

20912{
20913 /*
20914 * Based on the table's existing constraints, determine whether or not we
20915 * may skip scanning the table.
20916 */
20918 {
20919 if (!validate_default)
20921 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20922 RelationGetRelationName(scanrel))));
20923 else
20925 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20926 RelationGetRelationName(scanrel))));
20927 return;
20928 }
20929
20930 /*
20931 * Constraints proved insufficient. For plain relations, queue a
20932 * validation item now; for partitioned tables, recurse to process each
20933 * partition.
20934 */
20935 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20936 {
20937 AlteredTableInfo *tab;
20938
20939 /* Grab a work queue entry. */
20940 tab = ATGetQueueEntry(wqueue, scanrel);
20943 tab->validate_default = validate_default;
20944 }
20945 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20946 {
20947 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20948 int i;
20949
20950 for (i = 0; i < partdesc->nparts; i++)
20951 {
20954
20955 /*
20956 * This is the minimum lock we need to prevent deadlocks.
20957 */
20959
20960 /*
20961 * Adjust the constraint for scanrel so that it matches this
20962 * partition's attribute numbers.
20963 */
20966 part_rel, scanrel);
20967
20970 validate_default);
20971 table_close(part_rel, NoLock); /* keep lock till commit */
20972 }
20973 }
20974}
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 20301 of file tablecmds.c.

20303{
20304 Node *stmt = (Node *) arg;
20305 ObjectType reltype;
20306 HeapTuple tuple;
20309 char relkind;
20310
20311 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
20312 if (!HeapTupleIsValid(tuple))
20313 return; /* concurrently dropped */
20315 relkind = classform->relkind;
20316
20317 /* Must own relation. */
20320
20321 /*
20322 * Conflict log tables are used internally for logical replication
20323 * conflict logging and should not be altered directly, as it could
20324 * disrupt conflict logging.
20325 */
20327 ereport(ERROR,
20329 errmsg("cannot alter conflict log table \"%s\"",
20330 rv->relname),
20331 errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
20332
20333 /* No system table modifications unless explicitly allowed. */
20335 ereport(ERROR,
20337 errmsg("permission denied: \"%s\" is a system catalog",
20338 rv->relname)));
20339
20340 /*
20341 * Extract the specified relation type from the statement parse tree.
20342 *
20343 * Also, for ALTER .. RENAME, check permissions: the user must (still)
20344 * have CREATE rights on the containing namespace.
20345 */
20346 if (IsA(stmt, RenameStmt))
20347 {
20350 if (aclresult != ACLCHECK_OK)
20352 get_namespace_name(classform->relnamespace));
20353 reltype = ((RenameStmt *) stmt)->renameType;
20354 }
20355 else if (IsA(stmt, AlterObjectSchemaStmt))
20356 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
20357
20358 else if (IsA(stmt, AlterTableStmt))
20359 reltype = ((AlterTableStmt *) stmt)->objtype;
20360 else
20361 {
20362 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
20363 reltype = OBJECT_TABLE; /* placate compiler */
20364 }
20365
20366 /*
20367 * For compatibility with prior releases, we allow ALTER TABLE to be used
20368 * with most other types of relations (but not composite types). We allow
20369 * similar flexibility for ALTER INDEX in the case of RENAME, but not
20370 * otherwise. Otherwise, the user must select the correct form of the
20371 * command for the relation at issue.
20372 */
20373 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
20374 ereport(ERROR,
20376 errmsg("\"%s\" is not a sequence", rv->relname)));
20377
20378 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
20379 ereport(ERROR,
20381 errmsg("\"%s\" is not a view", rv->relname)));
20382
20383 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
20384 ereport(ERROR,
20386 errmsg("\"%s\" is not a materialized view", rv->relname)));
20387
20388 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
20389 ereport(ERROR,
20391 errmsg("\"%s\" is not a foreign table", rv->relname)));
20392
20393 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
20394 ereport(ERROR,
20396 errmsg("\"%s\" is not a composite type", rv->relname)));
20397
20398 if (reltype == OBJECT_PROPGRAPH && relkind != RELKIND_PROPGRAPH)
20399 ereport(ERROR,
20401 errmsg("\"%s\" is not a property graph", rv->relname)));
20402
20403 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
20404 relkind != RELKIND_PARTITIONED_INDEX
20405 && !IsA(stmt, RenameStmt))
20406 ereport(ERROR,
20408 errmsg("\"%s\" is not an index", rv->relname)));
20409
20410 /*
20411 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
20412 * TYPE for that.
20413 */
20414 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
20415 ereport(ERROR,
20417 errmsg("\"%s\" is a composite type", rv->relname),
20418 /* translator: %s is an SQL ALTER command */
20419 errhint("Use %s instead.",
20420 "ALTER TYPE")));
20421
20422 /*
20423 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
20424 * to a different schema, such as indexes and TOAST tables.
20425 */
20427 {
20428 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
20429 ereport(ERROR,
20431 errmsg("cannot change schema of index \"%s\"",
20432 rv->relname),
20433 errhint("Change the schema of the table instead.")));
20434 else if (relkind == RELKIND_COMPOSITE_TYPE)
20435 ereport(ERROR,
20437 errmsg("cannot change schema of composite type \"%s\"",
20438 rv->relname),
20439 /* translator: %s is an SQL ALTER command */
20440 errhint("Use %s instead.",
20441 "ALTER TYPE")));
20442 else if (relkind == RELKIND_TOASTVALUE)
20443 ereport(ERROR,
20445 errmsg("cannot change schema of TOAST table \"%s\"",
20446 rv->relname),
20447 errhint("Change the schema of the table instead.")));
20448 }
20449
20450 ReleaseSysCache(tuple);
20451}
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(), errdetail(), errhint(), errmsg, ERROR, fb(), get_namespace_name(), get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), HeapTupleIsValid, IsA, IsConflictLogTableClass(), 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 22313 of file tablecmds.c.

22315{
22318 HeapTuple tuple;
22319
22320 state = (struct AttachIndexCallbackState *) arg;
22321
22322 if (!state->lockedParentTbl)
22323 {
22324 LockRelationOid(state->parentTblOid, AccessShareLock);
22325 state->lockedParentTbl = true;
22326 }
22327
22328 /*
22329 * If we previously locked some other heap, and the name we're looking up
22330 * no longer refers to an index on that relation, release the now-useless
22331 * lock. XXX maybe we should do *after* we verify whether the index does
22332 * not actually belong to the same relation ...
22333 */
22334 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
22335 {
22336 UnlockRelationOid(state->partitionOid, AccessShareLock);
22337 state->partitionOid = InvalidOid;
22338 }
22339
22340 /* Didn't find a relation, so no need for locking or permission checks. */
22341 if (!OidIsValid(relOid))
22342 return;
22343
22344 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
22345 if (!HeapTupleIsValid(tuple))
22346 return; /* concurrently dropped, so nothing to do */
22348 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
22349 classform->relkind != RELKIND_INDEX)
22350 ereport(ERROR,
22352 errmsg("\"%s\" is not an index", rv->relname)));
22353 ReleaseSysCache(tuple);
22354
22355 /*
22356 * Since we need only examine the heap's tupledesc, an access share lock
22357 * on it (preventing any DDL) is sufficient.
22358 */
22359 state->partitionOid = IndexGetRelation(relOid, false);
22360 LockRelationOid(state->partitionOid, AccessShareLock);
22361}
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 1765 of file tablecmds.c.

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

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

4083{
4084 HeapTuple tuple;
4086
4087 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
4088 if (!HeapTupleIsValid(tuple))
4089 return; /* concurrently dropped */
4090 form = (Form_pg_class) GETSTRUCT(tuple);
4091 renameatt_check(relid, form, false);
4092 ReleaseSysCache(tuple);
4093}
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition tablecmds.c:3874

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

20235{
20236 HeapTuple tuple;
20237
20238 /* Nothing to do if the relation was not found. */
20239 if (!OidIsValid(relId))
20240 return;
20241
20242 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
20243 if (!HeapTupleIsValid(tuple)) /* should not happen */
20244 elog(ERROR, "cache lookup failed for relation %u", relId);
20245
20248
20249 ReleaseSysCache(tuple);
20250}

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

20199{
20200 char relkind;
20202
20203 /* Nothing to do if the relation was not found. */
20204 if (!OidIsValid(relId))
20205 return;
20206
20207 /*
20208 * If the relation does exist, check whether it's an index. But note that
20209 * the relation might have been dropped between the time we did the name
20210 * lookup and now. In that case, there's nothing to do.
20211 */
20212 relkind = get_rel_relkind(relId);
20213 if (!relkind)
20214 return;
20215 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
20216 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
20217 ereport(ERROR,
20219 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
20220
20221 /* Check permissions */
20223 if (aclresult != ACLCHECK_OK)
20226 relation->relname);
20227}
#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 20257 of file tablecmds.c.

20259{
20260 HeapTuple tuple;
20261
20262 /* Nothing to do if the relation was not found. */
20263 if (!OidIsValid(relId))
20264 return;
20265
20266 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
20267 if (!HeapTupleIsValid(tuple)) /* should not happen */
20268 elog(ERROR, "cache lookup failed for relation %u", relId);
20269
20272 relation->relname);
20273
20274 /*
20275 * Conflict log tables are used internally for logical replication
20276 * conflict logging and should not be modified directly, as it could
20277 * disrupt conflict logging.
20278 */
20280 ereport(ERROR,
20282 errmsg("cannot change conflict log table \"%s\"",
20283 relation->relname),
20284 errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
20285
20286 if (!allowSystemTableMods &&
20287 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
20288 ereport(ERROR,
20290 errmsg("permission denied: \"%s\" is a system catalog",
20291 relation->relname)));
20292
20293 ReleaseSysCache(tuple);
20294}

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, elog, ereport, errcode(), errdetail(), errmsg, ERROR, fb(), get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), HeapTupleIsValid, IsConflictLogTableClass(), 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 16547 of file tablecmds.c.

16550{
16551 CommentStmt *cmd;
16552 char *comment_str;
16554
16555 /* Look for comment for object wanted, and leave if none */
16557 if (comment_str == NULL)
16558 return;
16559
16560 /* Build CommentStmt node, copying all input data for safety */
16561 cmd = makeNode(CommentStmt);
16562 if (rel)
16563 {
16565 cmd->object = (Node *)
16568 makeString(pstrdup(conname)));
16569 }
16570 else
16571 {
16573 cmd->object = (Node *)
16575 makeString(pstrdup(conname)));
16576 }
16577 cmd->comment = comment_str;
16578
16579 /* Append it to list of commands */
16581 newcmd->subtype = AT_ReAddComment;
16582 newcmd->def = (Node *) cmd;
16583 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
16584}
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 22540 of file tablecmds.c.

22541{
22543
22544 existingIdx = index_get_partition(partitionTbl,
22545 RelationGetRelid(parentIdx));
22547 ereport(ERROR,
22549 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22551 RelationGetRelationName(parentIdx)),
22552 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
22554 RelationGetRelationName(partitionTbl))));
22555}

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

19965{
19968
19969 /*
19970 * We needn't bother registering the relation unless there is an ON COMMIT
19971 * action we need to take.
19972 */
19973 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19974 return;
19975
19977
19979 oc->relid = relid;
19980 oc->oncommit = action;
19981 oc->creating_subid = GetCurrentSubTransactionId();
19982 oc->deleting_subid = InvalidSubTransactionId;
19983
19984 /*
19985 * We use lcons() here so that ON COMMIT actions are processed in reverse
19986 * order of registration. That might not be essential but it seems
19987 * reasonable.
19988 */
19990
19992}
List * lcons(void *datum, List *list)
Definition list.c:495
MemoryContext CacheMemoryContext
Definition mcxt.c:170

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

Referenced by heap_create_with_catalog().

◆ relation_mark_replica_identity()

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

Definition at line 19105 of file tablecmds.c.

19107{
19114 ListCell *index;
19115
19116 /*
19117 * Check whether relreplident has changed, and update it if so.
19118 */
19123 elog(ERROR, "cache lookup failed for relation \"%s\"",
19126 if (pg_class_form->relreplident != ri_type)
19127 {
19128 pg_class_form->relreplident = ri_type;
19130 }
19133
19134 /*
19135 * Update the per-index indisreplident flags correctly.
19136 */
19138 foreach(index, RelationGetIndexList(rel))
19139 {
19141 bool dirty = false;
19142
19146 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
19148
19149 if (thisIndexOid == indexOid)
19150 {
19151 /* Set the bit if not already set. */
19152 if (!pg_index_form->indisreplident)
19153 {
19154 dirty = true;
19155 pg_index_form->indisreplident = true;
19156 }
19157 }
19158 else
19159 {
19160 /* Unset the bit if set. */
19161 if (pg_index_form->indisreplident)
19162 {
19163 dirty = true;
19164 pg_index_form->indisreplident = false;
19165 }
19166 }
19167
19168 if (dirty)
19169 {
19172 InvalidOid, is_internal);
19173
19174 /*
19175 * Invalidate the relcache for the table, so that after we commit
19176 * all sessions will refresh the table's replica identity index
19177 * before attempting any UPDATE or DELETE on the table. (If we
19178 * changed the table's pg_class row above, then a relcache inval
19179 * is already queued due to that; but we might not have.)
19180 */
19182 }
19184 }
19185
19187}
Definition type.h:97

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

Referenced by ATExecReplicaIdentity().

◆ RememberAllDependentForRebuilding()

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

Definition at line 15580 of file tablecmds.c.

15582{
15584 ScanKeyData key[3];
15585 SysScanDesc scan;
15587
15588 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15589
15591
15592 ScanKeyInit(&key[0],
15596 ScanKeyInit(&key[1],
15600 ScanKeyInit(&key[2],
15604
15606 NULL, 3, key);
15607
15609 {
15612
15613 foundObject.classId = foundDep->classid;
15614 foundObject.objectId = foundDep->objid;
15615 foundObject.objectSubId = foundDep->objsubid;
15616
15617 switch (foundObject.classId)
15618 {
15619 case RelationRelationId:
15620 {
15621 char relKind = get_rel_relkind(foundObject.objectId);
15622
15623 if (relKind == RELKIND_INDEX ||
15625 {
15626 Assert(foundObject.objectSubId == 0);
15628 }
15629 else if (relKind == RELKIND_SEQUENCE)
15630 {
15631 /*
15632 * This must be a SERIAL column's sequence. We need
15633 * not do anything to it.
15634 */
15635 Assert(foundObject.objectSubId == 0);
15636 }
15637 else
15638 {
15639 /* Not expecting any other direct dependencies... */
15640 elog(ERROR, "unexpected object depending on column: %s",
15642 }
15643 break;
15644 }
15645
15647 Assert(foundObject.objectSubId == 0);
15649 break;
15650
15652
15653 /*
15654 * A new-style SQL function can depend on a column, if that
15655 * column is referenced in the parsed function body. Ideally
15656 * we'd automatically update the function by deparsing and
15657 * reparsing it, but that's risky and might well fail anyhow.
15658 * FIXME someday.
15659 *
15660 * This is only a problem for AT_AlterColumnType, not
15661 * AT_SetExpression.
15662 */
15663 if (subtype == AT_AlterColumnType)
15664 ereport(ERROR,
15666 errmsg("cannot alter type of a column used by a function or procedure"),
15667 errdetail("%s depends on column \"%s\"",
15669 colName)));
15670 break;
15671
15672 case RewriteRelationId:
15673
15674 /*
15675 * View/rule bodies have pretty much the same issues as
15676 * function bodies. FIXME someday.
15677 */
15678 if (subtype == AT_AlterColumnType)
15679 ereport(ERROR,
15681 errmsg("cannot alter type of a column used by a view or rule"),
15682 errdetail("%s depends on column \"%s\"",
15684 colName)));
15685 break;
15686
15687 case TriggerRelationId:
15688
15689 /*
15690 * A trigger can depend on a column because the column is
15691 * specified as an update target, or because the column is
15692 * used in the trigger's WHEN condition. The first case would
15693 * not require any extra work, but the second case would
15694 * require updating the WHEN expression, which has the same
15695 * issues as above. Since we can't easily tell which case
15696 * applies, we punt for both. FIXME someday.
15697 */
15698 if (subtype == AT_AlterColumnType)
15699 ereport(ERROR,
15701 errmsg("cannot alter type of a column used in a trigger definition"),
15702 errdetail("%s depends on column \"%s\"",
15704 colName)));
15705 break;
15706
15707 case PolicyRelationId:
15708
15709 /*
15710 * A policy can depend on a column because the column is
15711 * specified in the policy's USING or WITH CHECK qual
15712 * expressions. It might be possible to rewrite and recheck
15713 * the policy expression, but punt for now. It's certainly
15714 * easy enough to remove and recreate the policy; still, FIXME
15715 * someday.
15716 */
15717 if (subtype == AT_AlterColumnType)
15718 ereport(ERROR,
15720 errmsg("cannot alter type of a column used in a policy definition"),
15721 errdetail("%s depends on column \"%s\"",
15723 colName)));
15724 break;
15725
15727 {
15729
15730 if (col.objectId == RelationGetRelid(rel) &&
15731 col.objectSubId == attnum)
15732 {
15733 /*
15734 * Ignore the column's own default expression. The
15735 * caller deals with it.
15736 */
15737 }
15738 else
15739 {
15740 /*
15741 * This must be a reference from the expression of a
15742 * generated column elsewhere in the same table.
15743 * Changing the type/generated expression of a column
15744 * that is used by a generated column is not allowed
15745 * by SQL standard, so just punt for now. It might be
15746 * doable with some thinking and effort.
15747 */
15748 if (subtype == AT_AlterColumnType)
15749 ereport(ERROR,
15751 errmsg("cannot alter type of a column used by a generated column"),
15752 errdetail("Column \"%s\" is used by generated column \"%s\".",
15753 colName,
15754 get_attname(col.objectId,
15755 col.objectSubId,
15756 false))));
15757 }
15758 break;
15759 }
15760
15762
15763 /*
15764 * Give the extended-stats machinery a chance to fix anything
15765 * that this column type change would break.
15766 */
15768 break;
15769
15771
15772 /*
15773 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15774 * clause. Same issues as above. FIXME someday.
15775 */
15776 if (subtype == AT_AlterColumnType)
15777 ereport(ERROR,
15779 errmsg("cannot alter type of a column used by a publication WHERE clause"),
15780 errdetail("%s depends on column \"%s\"",
15782 colName)));
15783 break;
15784
15785 default:
15786
15787 /*
15788 * We don't expect any other sorts of objects to depend on a
15789 * column.
15790 */
15791 elog(ERROR, "unexpected object depending on column: %s",
15793 break;
15794 }
15795 }
15796
15797 systable_endscan(scan);
15799}
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 15989 of file tablecmds.c.

15990{
15991 if (!get_index_isclustered(indoid))
15992 return;
15993
15994 if (tab->clusterOnIndex)
15995 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15996
15997 tab->clusterOnIndex = get_rel_name(indoid);
15998}
bool get_index_isclustered(Oid index_oid)
Definition lsyscache.c:3962

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

16006{
16007 /*
16008 * This de-duplication check is critical for two independent reasons: we
16009 * mustn't try to recreate the same constraint twice, and if a constraint
16010 * depends on more than one column whose type is to be altered, we must
16011 * capture its definition string before applying any of the column type
16012 * changes. ruleutils.c will get confused if we ask again later.
16013 */
16014 if (!list_member_oid(tab->changedConstraintOids, conoid))
16015 {
16016 /* OK, capture the constraint's existing definition string */
16018 Oid indoid;
16019
16020 /*
16021 * It is critical to create not-null constraints ahead of primary key
16022 * indexes; otherwise, the not-null constraint would be created by the
16023 * primary key, and the constraint name would be wrong.
16024 */
16026 {
16027 tab->changedConstraintOids = lcons_oid(conoid,
16031 }
16032 else
16033 {
16034
16036 conoid);
16038 defstring);
16039 }
16040
16041 /*
16042 * For the index of a constraint, if any, remember if it is used for
16043 * the table's replica identity or if it is a clustered index, so that
16044 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
16045 * those properties.
16046 */
16047 indoid = get_constraint_index(conoid);
16048 if (OidIsValid(indoid))
16049 {
16051 RememberClusterOnForRebuilding(indoid, tab);
16052 }
16053 }
16054}
List * lcons_oid(Oid datum, List *list)
Definition list.c:531
char * pg_get_constraintdef_command(Oid constraintId)
Definition ruleutils.c:2544
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(), RememberIndexForRebuilding(), and RememberWholeRowDependentForRebuilding().

◆ RememberIndexForRebuilding()

static void RememberIndexForRebuilding ( Oid  indoid,
AlteredTableInfo tab 
)
static

Definition at line 16061 of file tablecmds.c.

16062{
16063 /*
16064 * This de-duplication check is critical for two independent reasons: we
16065 * mustn't try to recreate the same index twice, and if an index depends
16066 * on more than one column whose type is to be altered, we must capture
16067 * its definition string before applying any of the column type changes.
16068 * ruleutils.c will get confused if we ask again later.
16069 */
16070 if (!list_member_oid(tab->changedIndexOids, indoid))
16071 {
16072 /*
16073 * Before adding it as an index-to-rebuild, we'd better see if it
16074 * belongs to a constraint, and if so rebuild the constraint instead.
16075 * Typically this check fails, because constraint indexes normally
16076 * have only dependencies on their constraint. But it's possible for
16077 * such an index to also have direct dependencies on table columns,
16078 * for example with a partial exclusion constraint.
16079 */
16080 Oid conoid = get_index_constraint(indoid);
16081
16082 if (OidIsValid(conoid))
16083 {
16085 }
16086 else
16087 {
16088 /* OK, capture the index's existing definition string */
16089 char *defstring = pg_get_indexdef_string(indoid);
16090
16092 indoid);
16094 defstring);
16095
16096 /*
16097 * Remember if this index is used for the table's replica identity
16098 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
16099 * can queue up commands necessary to restore those properties.
16100 */
16102 RememberClusterOnForRebuilding(indoid, tab);
16103 }
16104 }
16105}
Oid get_index_constraint(Oid indexId)
Definition pg_depend.c:1192
char * pg_get_indexdef_string(Oid indexrelid)
Definition ruleutils.c:1238

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

◆ RememberReplicaIdentityForRebuilding()

static void RememberReplicaIdentityForRebuilding ( Oid  indoid,
AlteredTableInfo tab 
)
static

Definition at line 15974 of file tablecmds.c.

15975{
15976 if (!get_index_isreplident(indoid))
15977 return;
15978
15979 if (tab->replicaIdentityIndex)
15980 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15981
15982 tab->replicaIdentityIndex = get_rel_name(indoid);
15983}
bool get_index_isreplident(Oid index_oid)
Definition lsyscache.c:3916

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

16113{
16114 /*
16115 * This de-duplication check is critical for two independent reasons: we
16116 * mustn't try to recreate the same statistics object twice, and if the
16117 * statistics object depends on more than one column whose type is to be
16118 * altered, we must capture its definition string before applying any of
16119 * the type changes. ruleutils.c will get confused if we ask again later.
16120 */
16122 {
16123 /* OK, capture the statistics object's existing definition string */
16125
16127 stxoid);
16129 defstring);
16130 }
16131}
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition ruleutils.c:1987

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

Referenced by RememberAllDependentForRebuilding().

◆ RememberWholeRowDependentForRebuilding()

static void RememberWholeRowDependentForRebuilding ( AlteredTableInfo tab,
AlterTableType  subtype,
Relation  rel 
)
static

Definition at line 15811 of file tablecmds.c.

15812{
15820 bool isnull;
15821
15822 Assert(subtype == AT_SetExpression);
15823
15824 /*
15825 * Check CHECK constraints with whole-row references first.
15826 */
15827 if (RelationGetDescr(rel)->constr &&
15828 RelationGetDescr(rel)->constr->num_check > 0)
15829 {
15831
15836
15839 true,
15840 NULL,
15841 1,
15842 &skey);
15843
15845 {
15848
15849 if (conform->contype != CONSTRAINT_CHECK)
15850 continue;
15851
15855 &isnull);
15856 if (isnull)
15857 elog(ERROR, "null conbin for relation \"%s\"",
15859 else
15860 {
15861 char *exprString;
15862 Node *expr;
15864
15866 expr = stringToNode(exprString);
15868
15869 /* Find all attributes referenced */
15870 pull_varattnos(expr, 1, &expr_attrs);
15871
15872 /*
15873 * If the CHECK constraint contains whole-row reference then
15874 * remember it.
15875 */
15877 {
15879 }
15880 }
15881 }
15884 }
15885
15886 /*
15887 * Now check indexes with whole-row references. Prepare to scan pg_index
15888 * for entries having indrelid matching this relation.
15889 */
15894
15896
15899 true,
15900 NULL,
15901 1,
15902 &skey);
15903
15905 {
15908
15912 &isnull);
15913 if (!isnull)
15914 {
15915 char *exprString;
15916 Node *expr;
15918
15920 expr = stringToNode(exprString);
15922
15923 /* Find all attributes referenced */
15924 pull_varattnos(expr, 1, &expr_attrs);
15925
15926 /*
15927 * If the index expression contains a whole-row reference then
15928 * remember it.
15929 */
15931 {
15932 RememberIndexForRebuilding(index->indexrelid, tab);
15933 continue;
15934 }
15935 }
15936
15940 &isnull);
15941 if (!isnull)
15942 {
15943 char *exprString;
15944 Node *expr;
15946
15948 expr = stringToNode(exprString);
15950
15951 /* Find all attributes referenced */
15952 pull_varattnos(expr, 1, &expr_attrs);
15953
15954 /*
15955 * If the index predicate expression contains a whole-row
15956 * reference then remember it.
15957 */
15959 {
15960 RememberIndexForRebuilding(index->indexrelid, tab);
15961 }
15962 }
15963 }
15964
15967}

References AccessShareLock, Assert, AT_SetExpression, bms_is_member(), BTEqualStrategyNumber, elog, ERROR, fb(), FirstLowInvalidHeapAttributeNumber, Form_pg_constraint, Form_pg_index, GETSTRUCT(), heap_getattr(), HeapTupleIsValid, InvalidAttrNumber, ObjectIdGetDatum(), pfree(), pull_varattnos(), RelationGetDescr, RelationGetRelationName, RelationGetRelid, RememberConstraintForRebuilding(), RememberIndexForRebuilding(), ScanKeyInit(), stringToNode(), systable_beginscan(), systable_endscan(), systable_getnext(), table_close(), table_open(), and TextDatumGetCString.

Referenced by ATExecSetExpression().

◆ remove_on_commit_action()

void remove_on_commit_action ( Oid  relid)

Definition at line 20000 of file tablecmds.c.

20001{
20002 ListCell *l;
20003
20004 foreach(l, on_commits)
20005 {
20007
20008 if (oc->relid == relid)
20009 {
20011 break;
20012 }
20013 }
20014}
SubTransactionId deleting_subid
Definition tablecmds.c:132

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

Referenced by heap_drop_with_catalog().

◆ RemoveInheritance()

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

Definition at line 18653 of file tablecmds.c.

18654{
18656 SysScanDesc scan;
18657 ScanKeyData key[3];
18660 AttrMap *attmap;
18661 List *connames;
18662 List *nncolumns;
18663 bool found;
18664 bool is_partitioning;
18665
18667
18672 if (!found)
18673 {
18674 if (is_partitioning)
18675 ereport(ERROR,
18677 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18680 else
18681 ereport(ERROR,
18683 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18686 }
18687
18688 /*
18689 * Search through child columns looking for ones matching parent rel
18690 */
18692 ScanKeyInit(&key[0],
18697 true, NULL, 1, key);
18699 {
18701
18702 /* Ignore if dropped or not inherited */
18703 if (att->attisdropped)
18704 continue;
18705 if (att->attinhcount <= 0)
18706 continue;
18707
18709 NameStr(att->attname)))
18710 {
18711 /* Decrement inhcount and possibly set islocal to true */
18714
18715 copy_att->attinhcount--;
18716 if (copy_att->attinhcount == 0)
18717 copy_att->attislocal = true;
18718
18721 }
18722 }
18723 systable_endscan(scan);
18725
18726 /*
18727 * Likewise, find inherited check and not-null constraints and disinherit
18728 * them. To do this, we first need a list of the names of the parent's
18729 * check constraints. (We cheat a bit by only checking for name matches,
18730 * assuming that the expressions will match.)
18731 *
18732 * For NOT NULL columns, we store column numbers to match, mapping them in
18733 * to the child rel's attribute numbers.
18734 */
18737 false);
18738
18740 ScanKeyInit(&key[0],
18745 true, NULL, 1, key);
18746
18747 connames = NIL;
18748 nncolumns = NIL;
18749
18751 {
18753
18754 if (con->connoinherit)
18755 continue;
18756
18757 if (con->contype == CONSTRAINT_CHECK)
18758 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18759 if (con->contype == CONSTRAINT_NOTNULL)
18760 {
18762
18764 }
18765 }
18766
18767 systable_endscan(scan);
18768
18769 /* Now scan the child's constraints to find matches */
18770 ScanKeyInit(&key[0],
18775 true, NULL, 1, key);
18776
18778 {
18780 bool match = false;
18781
18782 /*
18783 * Match CHECK constraints by name, not-null constraints by column
18784 * number, and ignore all others.
18785 */
18786 if (con->contype == CONSTRAINT_CHECK)
18787 {
18789 {
18790 if (con->contype == CONSTRAINT_CHECK &&
18791 strcmp(NameStr(con->conname), chkname) == 0)
18792 {
18793 match = true;
18795 break;
18796 }
18797 }
18798 }
18799 else if (con->contype == CONSTRAINT_NOTNULL)
18800 {
18802
18804 {
18805 if (prevattno == child_attno)
18806 {
18807 match = true;
18809 break;
18810 }
18811 }
18812 }
18813 else
18814 continue;
18815
18816 if (match)
18817 {
18818 /* Decrement inhcount and possibly set islocal to true */
18821
18822 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18823 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18825
18826 copy_con->coninhcount--;
18827 if (copy_con->coninhcount == 0)
18828 copy_con->conislocal = true;
18829
18832 }
18833 }
18834
18835 /* We should have matched all constraints */
18836 if (connames != NIL || nncolumns != NIL)
18837 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18840
18841 systable_endscan(scan);
18843
18848
18849 /*
18850 * Post alter hook of this inherits. Since object_access_hook doesn't take
18851 * multiple object identifiers, we relay oid of parent relation using
18852 * auxiliary_id argument.
18853 */
18857}
bool DeleteInheritsTuple(Oid inhrelid, Oid inhparent, bool expect_detach_pending, const char *childname)
bool SearchSysCacheExistsAttName(Oid relid, const char *attname)
Definition syscache.c:518
#define child_dependency_type(child_is_partition)
Definition tablecmds.c:396

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

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

◆ RemoveInheritedConstraint()

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

Definition at line 12069 of file tablecmds.c.

12071{
12075 SysScanDesc scan;
12077
12078 ScanKeyInit(&key,
12081 ObjectIdGetDatum(conrelid));
12082
12085 true, NULL, 1, &key);
12087 while ((consttup = systable_getnext(scan)) != NULL)
12088 {
12090
12091 if (conform->conparentid != conoid)
12092 continue;
12093 else
12094 {
12095 ObjectAddress addr;
12099
12102
12103 /*
12104 * First we must delete the dependency record that binds the
12105 * constraint records together.
12106 */
12108 conform->oid,
12111 conoid);
12112 Assert(n == 1); /* actually only one is expected */
12113
12114 /*
12115 * Now search for the triggers for this constraint and set them up
12116 * for deletion too
12117 */
12123 true, NULL, 1, &key2);
12124 while ((trigtup = systable_getnext(scan2)) != NULL)
12125 {
12129 }
12131 }
12132 }
12133 /* make the dependency deletions visible */
12137 systable_endscan(scan);
12138}
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:308
long deleteDependencyRecordsForSpecific(Oid classId, Oid objectId, char deptype, Oid refclassId, Oid refobjectId)
Definition pg_depend.c:411

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

Referenced by AttachPartitionForeignKey().

◆ RemoveRelations()

void RemoveRelations ( DropStmt drop)

Definition at line 1597 of file tablecmds.c.

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

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

4146{
4149 HeapTuple tuple;
4151 ObjectAddress address;
4152
4153 Assert(!myrelid || !mytypid);
4154
4155 if (mytypid)
4156 {
4158 }
4159 else
4160 {
4162
4163 /*
4164 * don't tell it whether we're recursing; we allow changing typed
4165 * tables here
4166 */
4168
4170 }
4171
4173 if (!HeapTupleIsValid(tuple))
4174 elog(ERROR, "cache lookup failed for constraint %u",
4176 con = (Form_pg_constraint) GETSTRUCT(tuple);
4177
4178 if (myrelid &&
4179 (con->contype == CONSTRAINT_CHECK ||
4180 con->contype == CONSTRAINT_NOTNULL) &&
4181 !con->connoinherit)
4182 {
4183 if (recurse)
4184 {
4187 ListCell *lo,
4188 *li;
4189
4192
4194 {
4196 int numparents = lfirst_int(li);
4197
4198 if (childrelid == myrelid)
4199 continue;
4200
4202 }
4203 }
4204 else
4205 {
4206 if (expected_parents == 0 &&
4208 ereport(ERROR,
4210 errmsg("inherited constraint \"%s\" must be renamed in child tables too",
4211 oldconname)));
4212 }
4213
4214 if (con->coninhcount > expected_parents)
4215 ereport(ERROR,
4217 errmsg("cannot rename inherited constraint \"%s\"",
4218 oldconname)));
4219 }
4220
4221 if (con->conindid
4222 && (con->contype == CONSTRAINT_PRIMARY
4223 || con->contype == CONSTRAINT_UNIQUE
4224 || con->contype == CONSTRAINT_EXCLUSION))
4225 /* rename the index; this renames the constraint as well */
4226 RenameRelationInternal(con->conindid, newconname, false, true);
4227 else
4229
4231
4232 ReleaseSysCache(tuple);
4233
4234 if (targetrelation)
4235 {
4236 /*
4237 * Invalidate relcache so as others can see the new constraint name.
4238 */
4240
4241 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4242 }
4243
4244 return address;
4245}
void RenameConstraintById(Oid conId, const char *newname)
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:4139

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

4102{
4103 Oid relid;
4105 ObjectAddress address;
4106
4107 /* lock level taken here should match renameatt_internal */
4109 stmt->missing_ok ? RVR_MISSING_OK : 0,
4111 NULL);
4112
4113 if (!OidIsValid(relid))
4114 {
4116 (errmsg("relation \"%s\" does not exist, skipping",
4117 stmt->relation->relname)));
4118 return InvalidObjectAddress;
4119 }
4120
4121 attnum =
4122 renameatt_internal(relid,
4123 stmt->subname, /* old att name */
4124 stmt->newname, /* new att name */
4125 stmt->relation->inh, /* recursive? */
4126 false, /* recursing? */
4127 0, /* expected inhcount */
4128 stmt->behavior);
4129
4131
4132 return address;
4133}
static AttrNumber renameatt_internal(Oid myrelid, const char *oldattname, const char *newattname, bool recurse, bool recursing, int expected_parents, DropBehavior behavior)
Definition tablecmds.c:3936
static void RangeVarCallbackForRenameAttribute(const RangeVar *rv, Oid relid, Oid oldrelid, void *arg)
Definition tablecmds.c:4081

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

3875{
3876 char relkind = classform->relkind;
3877
3878 if (classform->reloftype && !recursing)
3879 ereport(ERROR,
3881 errmsg("cannot rename column of typed table")));
3882
3883 /*
3884 * Renaming the columns of sequences or toast tables doesn't actually
3885 * break anything from the system's point of view, since internal
3886 * references are by attnum. But it doesn't seem right to allow users to
3887 * change names that are hardcoded into the system, hence the following
3888 * restriction.
3889 */
3890 if (relkind != RELKIND_RELATION &&
3891 relkind != RELKIND_VIEW &&
3892 relkind != RELKIND_MATVIEW &&
3893 relkind != RELKIND_COMPOSITE_TYPE &&
3894 relkind != RELKIND_INDEX &&
3895 relkind != RELKIND_PARTITIONED_INDEX &&
3896 relkind != RELKIND_FOREIGN_TABLE &&
3897 relkind != RELKIND_PARTITIONED_TABLE)
3898 ereport(ERROR,
3900 errmsg("cannot rename columns of relation \"%s\"",
3901 NameStr(classform->relname)),
3903
3904 /*
3905 * permissions checking. only the owner of a class can change its schema.
3906 */
3909 NameStr(classform->relname));
3910
3911 /*
3912 * Conflict log tables are used internally for logical replication
3913 * conflict logging and should not be modified directly, as it could
3914 * disrupt conflict logging.
3915 */
3917 ereport(ERROR,
3919 errmsg("cannot rename columns of conflict log table \"%s\"",
3920 NameStr(classform->relname)),
3921 errdetail("Conflict log tables are system-managed tables for logical replication conflicts.")));
3922
3924 ereport(ERROR,
3926 errmsg("permission denied: \"%s\" is a system catalog",
3927 NameStr(classform->relname))));
3928}

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, ereport, errcode(), errdetail(), errdetail_relkind_not_supported(), errmsg, ERROR, fb(), get_rel_relkind(), get_relkind_objtype(), GetUserId(), IsConflictLogTableClass(), 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 3936 of file tablecmds.c.

3943{
3949
3950 /*
3951 * Grab an exclusive lock on the target table, which we will NOT release
3952 * until end of transaction.
3953 */
3956
3957 /*
3958 * if the 'recurse' flag is set then we are supposed to rename this
3959 * attribute in all classes that inherit from 'relname' (as well as in
3960 * 'relname').
3961 *
3962 * any permissions or problems with duplicate attributes will cause the
3963 * whole transaction to abort, which is what we want -- all or nothing.
3964 */
3965 if (recurse)
3966 {
3969 ListCell *lo,
3970 *li;
3971
3972 /*
3973 * we need the number of parents for each child so that the recursive
3974 * calls to renameatt() can determine whether there are any parents
3975 * outside the inheritance hierarchy being processed.
3976 */
3979
3980 /*
3981 * find_all_inheritors does the recursive search of the inheritance
3982 * hierarchy, so all we have to do is process all of the relids in the
3983 * list that it returns.
3984 */
3986 {
3988 int numparents = lfirst_int(li);
3989
3990 if (childrelid == myrelid)
3991 continue;
3992 /* note we need not recurse again */
3994 }
3995 }
3996 else
3997 {
3998 /*
3999 * If we are told not to recurse, there had better not be any child
4000 * tables; else the rename would put them out of step.
4001 *
4002 * expected_parents will only be 0 if we are not already recursing.
4003 */
4004 if (expected_parents == 0 &&
4006 ereport(ERROR,
4008 errmsg("inherited column \"%s\" must be renamed in child tables too",
4009 oldattname)));
4010 }
4011
4012 /* rename attributes in typed tables of composite type */
4013 if (targetrelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
4014 {
4016 ListCell *lo;
4017
4020 behavior);
4021
4022 foreach(lo, child_oids)
4023 renameatt_internal(lfirst_oid(lo), oldattname, newattname, true, true, 0, behavior);
4024 }
4025
4027
4030 ereport(ERROR,
4032 errmsg("column \"%s\" does not exist",
4033 oldattname)));
4035
4036 attnum = attform->attnum;
4037 if (attnum <= 0)
4038 ereport(ERROR,
4040 errmsg("cannot rename system column \"%s\"",
4041 oldattname)));
4042
4043 /*
4044 * if the attribute is inherited, forbid the renaming. if this is a
4045 * top-level call to renameatt(), then expected_parents will be 0, so the
4046 * effect of this code will be to prohibit the renaming if the attribute
4047 * is inherited at all. if this is a recursive call to renameatt(),
4048 * expected_parents will be the number of parents the current relation has
4049 * within the inheritance hierarchy being processed, so we'll prohibit the
4050 * renaming only if there are additional parents from elsewhere.
4051 */
4052 if (attform->attinhcount > expected_parents)
4053 ereport(ERROR,
4055 errmsg("cannot rename inherited column \"%s\"",
4056 oldattname)));
4057
4058 /* new name should not already exist */
4060
4061 /* apply the update */
4062 namestrcpy(&(attform->attname), newattname);
4063
4065
4067
4069
4071
4072 relation_close(targetrelation, NoLock); /* close rel but keep lock */
4073
4074 return attnum;
4075}
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 4248 of file tablecmds.c.

4249{
4250 Oid relid = InvalidOid;
4251 Oid typid = InvalidOid;
4252
4253 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4254 {
4255 Relation rel;
4256 HeapTuple tup;
4257
4261 if (!HeapTupleIsValid(tup))
4262 elog(ERROR, "cache lookup failed for type %u", typid);
4265 table_close(rel, NoLock);
4266 }
4267 else
4268 {
4269 /* lock level taken here should match rename_constraint_internal */
4271 stmt->missing_ok ? RVR_MISSING_OK : 0,
4273 NULL);
4274 if (!OidIsValid(relid))
4275 {
4277 (errmsg("relation \"%s\" does not exist, skipping",
4278 stmt->relation->relname)));
4279 return InvalidObjectAddress;
4280 }
4281 }
4282
4283 return
4284 rename_constraint_internal(relid, typid,
4285 stmt->subname,
4286 stmt->newname,
4287 (stmt->relation &&
4288 stmt->relation->inh), /* recursive? */
4289 false, /* recursing? */
4290 0 /* expected inhcount */ );
4291}
void checkDomainOwner(HeapTuple tup)
Definition typecmds.c:3525

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

4299{
4300 bool is_index_stmt = stmt->renameType == OBJECT_INDEX;
4301 Oid relid;
4302 ObjectAddress address;
4303
4304 /*
4305 * Grab an exclusive lock on the target table, index, sequence, view,
4306 * materialized view, or foreign table, which we will NOT release until
4307 * end of transaction.
4308 *
4309 * Lock level used here should match RenameRelationInternal, to avoid lock
4310 * escalation. However, because ALTER INDEX can be used with any relation
4311 * type, we mustn't believe without verification.
4312 */
4313 for (;;)
4314 {
4315 LOCKMODE lockmode;
4316 char relkind;
4317 bool obj_is_index;
4318
4320
4321 relid = RangeVarGetRelidExtended(stmt->relation, lockmode,
4322 stmt->missing_ok ? RVR_MISSING_OK : 0,
4324 stmt);
4325
4326 if (!OidIsValid(relid))
4327 {
4329 (errmsg("relation \"%s\" does not exist, skipping",
4330 stmt->relation->relname)));
4331 return InvalidObjectAddress;
4332 }
4333
4334 /*
4335 * We allow mismatched statement and object types (e.g., ALTER INDEX
4336 * to rename a table), but we might've used the wrong lock level. If
4337 * that happens, retry with the correct lock level. We don't bother
4338 * if we already acquired AccessExclusiveLock with an index, however.
4339 */
4340 relkind = get_rel_relkind(relid);
4341 obj_is_index = (relkind == RELKIND_INDEX ||
4342 relkind == RELKIND_PARTITIONED_INDEX);
4344 break;
4345
4346 UnlockRelationOid(relid, lockmode);
4348 }
4349
4350 /* Do the work */
4351 RenameRelationInternal(relid, stmt->newname, false, is_index_stmt);
4352
4353 ObjectAddressSet(address, RelationRelationId, relid);
4354
4355 return address;
4356}

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

4363{
4365 Relation relrelation; /* for RELATION relation */
4370
4371 /*
4372 * Grab a lock on the target relation, which we will NOT release until end
4373 * of transaction. We need at least a self-exclusive lock so that
4374 * concurrent DDL doesn't overwrite the rename if they start updating
4375 * while still seeing the old version. The lock also guards against
4376 * triggering relcache reloads in concurrent sessions, which might not
4377 * handle this information changing under them. For indexes, we can use a
4378 * reduced lock level because RelationReloadIndexInfo() handles indexes
4379 * specially.
4380 */
4383
4384 /*
4385 * Find relation's pg_class tuple, and make sure newrelname isn't in use.
4386 */
4388
4390 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4391 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4392 otid = reltup->t_self;
4394
4396 ereport(ERROR,
4398 errmsg("relation \"%s\" already exists",
4399 newrelname)));
4400
4401 /*
4402 * RenameRelation is careful not to believe the caller's idea of the
4403 * relation kind being handled. We don't have to worry about this, but
4404 * let's not be totally oblivious to it. We can process an index as
4405 * not-an-index, but not the other way around.
4406 */
4407 Assert(!is_index ||
4408 is_index == (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4409 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX));
4410
4411 /*
4412 * Update pg_class tuple with new relname. (Scribbling on reltup is OK
4413 * because it's a copy...)
4414 */
4415 namestrcpy(&(relform->relname), newrelname);
4416
4419
4421 InvalidOid, is_internal);
4422
4425
4426 /*
4427 * Also rename the associated type, if any.
4428 */
4429 if (OidIsValid(targetrelation->rd_rel->reltype))
4430 RenameTypeInternal(targetrelation->rd_rel->reltype,
4432
4433 /*
4434 * Also rename the associated constraint, if any.
4435 */
4436 if (targetrelation->rd_rel->relkind == RELKIND_INDEX ||
4437 targetrelation->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
4438 {
4440
4443 }
4444
4445 /*
4446 * Close rel, but keep lock!
4447 */
4449}
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 4455 of file tablecmds.c.

4456{
4457 Relation relrelation; /* for RELATION relation */
4460
4461 /*
4462 * Find relation's pg_class tuple.
4463 */
4465
4467 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4468 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4470
4471 /*
4472 * Update pg_class tuple.
4473 */
4474 relform->relrewrite = InvalidOid;
4475
4477
4480}

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

7983{
7984 Form_pg_attribute attr;
7986
7988
7990
7991 /*
7992 * Exit quickly by testing attnotnull from the tupledesc's copy of the
7993 * attribute.
7994 */
7995 attr = TupleDescAttr(RelationGetDescr(rel), attnum - 1);
7996 if (attr->attisdropped)
7997 return;
7998
7999 if (!attr->attnotnull)
8000 {
8002 HeapTuple tuple;
8003
8005
8007 if (!HeapTupleIsValid(tuple))
8008 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
8009 attnum, RelationGetRelid(rel));
8010
8012 thisatt->attnullability = ATTNULLABLE_VALID;
8013
8014 attr = (Form_pg_attribute) GETSTRUCT(tuple);
8015
8016 attr->attnotnull = true;
8017 CatalogTupleUpdate(attr_rel, &tuple->t_self, tuple);
8018
8019 /*
8020 * If the nullness isn't already proven by validated constraints, have
8021 * ALTER TABLE phase 3 test for it.
8022 */
8023 if (queue_validation && wqueue &&
8025 {
8026 AlteredTableInfo *tab;
8027
8028 tab = ATGetQueueEntry(wqueue, rel);
8029 tab->verify_new_notnull = true;
8030 }
8031
8033
8035 heap_freetuple(tuple);
8036 }
8037 else
8038 {
8040 }
8041}
static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr)
Definition tablecmds.c:8230

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

9270{
9271 ListCell *lc;
9272
9273 foreach(lc, RelationGetIndexList(rel))
9274 {
9275 Oid indexoid = lfirst_oid(lc);
9278 HeapTuple tuple;
9279
9280 indrel = index_open(indexoid, lockmode);
9281
9282 for (int i = 0; i < indrel->rd_index->indnatts; i++)
9283 {
9284 if (indrel->rd_index->indkey.values[i] == attnum)
9285 {
9286 indattnum = i + 1;
9287 break;
9288 }
9289 }
9290
9291 if (indattnum == 0)
9292 {
9293 index_close(indrel, lockmode);
9294 continue;
9295 }
9296
9298
9299 if (HeapTupleIsValid(tuple))
9300 {
9302
9303 if (setstorage)
9304 attrtuple->attstorage = newstorage;
9305
9306 if (setcompression)
9307 attrtuple->attcompression = newcompression;
9308
9309 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
9310
9312 RelationGetRelid(rel),
9313 attrtuple->attnum);
9314
9315 heap_freetuple(tuple);
9316 }
9317
9318 index_close(indrel, lockmode);
9319 }
9320}

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

3727{
3729 HeapTuple tuple;
3731
3733 ShareUpdateExclusiveLock, false) ||
3735 ShareRowExclusiveLock, true));
3736
3737 /*
3738 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3739 */
3742 if (!HeapTupleIsValid(tuple))
3743 elog(ERROR, "cache lookup failed for relation %u", relationId);
3745
3746 if (classtuple->relhassubclass != relhassubclass)
3747 {
3748 classtuple->relhassubclass = relhassubclass;
3750 }
3751 else
3752 {
3753 /* no need to change tuple, but force relcache rebuild anyway */
3755 }
3756
3757 heap_freetuple(tuple);
3759}
void CacheInvalidateRelcacheByTuple(HeapTuple classTuple)
Definition inval.c:1666

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

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

◆ SetRelationTableSpace()

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

Definition at line 3829 of file tablecmds.c.

3832{
3834 HeapTuple tuple;
3836 Form_pg_class rd_rel;
3837 Oid reloid = RelationGetRelid(rel);
3838
3840
3841 /* Get a modifiable copy of the relation's pg_class row. */
3843
3845 if (!HeapTupleIsValid(tuple))
3846 elog(ERROR, "cache lookup failed for relation %u", reloid);
3847 otid = tuple->t_self;
3848 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3849
3850 /* Update the pg_class row. */
3851 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3854 rd_rel->relfilenode = newRelFilenumber;
3857
3858 /*
3859 * Record dependency on tablespace. This is only required for relations
3860 * that have no physical storage.
3861 */
3862 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3864 rd_rel->reltablespace);
3865
3866 heap_freetuple(tuple);
3868}
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 24034 of file tablecmds.c.

24036{
24037 /* The FSM is empty, so don't bother using it. */
24038 uint32 ti_options = TABLE_INSERT_SKIP_FSM;
24039 CommandId mycid;
24040 EState *estate;
24042 *listptr2;
24044 ExprContext *econtext;
24045 TableScanDesc scan;
24046 Snapshot snapshot;
24051 *pc;
24052
24053 mycid = GetCurrentCommandId(true);
24054
24055 estate = CreateExecutorState();
24056
24057 forboth(listptr, partlist, listptr2, newPartRels)
24058 {
24060
24062
24063 /* Find the work queue entry for the new partition table: newPartRel. */
24064 pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
24065
24066 buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
24067
24068 if (sps->bound->is_default)
24069 {
24070 /*
24071 * We should not create a structure to check the partition
24072 * constraint for the new DEFAULT partition.
24073 */
24075 }
24076 else
24077 {
24079
24080 /* Build expression execution states for partition check quals. */
24084 (Node *) partConstraint);
24085 /* Make a boolean expression for ExecCheck(). */
24087
24088 /*
24089 * Map the vars in the constraint expression from rel's attnos to
24090 * splitRel's.
24091 */
24093 1, splitRel, rel);
24094
24095 pc->partqualstate =
24097 Assert(pc->partqualstate != NULL);
24098 }
24099
24100 /* Store partition context into a list. */
24102 }
24103
24104 econtext = GetPerTupleExprContext(estate);
24105
24106 /* Create the necessary tuple slot. */
24108
24109 /*
24110 * Map computing for moving attributes of the split partition to the new
24111 * partition (for the first new partition, but other new partitions can
24112 * use the same map).
24113 */
24116 RelationGetDescr(pc->partRel));
24117
24118 /* Scan through the rows. */
24119 snapshot = RegisterSnapshot(GetLatestSnapshot());
24120 scan = table_beginscan(splitRel, snapshot, 0, NULL,
24121 SO_NONE);
24122
24123 /*
24124 * Switch to per-tuple memory context and reset it for each tuple
24125 * produced, so we don't leak memory.
24126 */
24128
24130 {
24131 bool found = false;
24133
24135
24136 econtext->ecxt_scantuple = srcslot;
24137
24138 /* Search partition for the current slot, srcslot. */
24139 foreach(listptr, partContexts)
24140 {
24142
24143 /* skip DEFAULT partition */
24144 if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
24145 {
24146 found = true;
24147 break;
24148 }
24149 }
24150 if (!found)
24151 {
24152 /* Use the DEFAULT partition if it exists. */
24153 if (defaultPartCtx)
24155 else
24156 ereport(ERROR,
24158 errmsg("cannot find partition for split partition row"),
24160 }
24161
24162 if (tuple_map)
24163 {
24164 /* Need to use a map to copy attributes. */
24165 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
24166 }
24167 else
24168 {
24169 /* Extract data from the old tuple. */
24171
24172 /* Copy attributes directly. */
24173 insertslot = pc->dstslot;
24174
24176
24177 memcpy(insertslot->tts_values, srcslot->tts_values,
24178 sizeof(Datum) * srcslot->tts_nvalid);
24179 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
24180 sizeof(bool) * srcslot->tts_nvalid);
24181
24183 }
24184
24185 /*
24186 * Constraints and GENERATED expressions might reference the tableoid
24187 * column, so fill tts_tableOid with the desired value. (We must do
24188 * this each time, because it gets overwritten with newrel's OID
24189 * during storing.)
24190 */
24191 insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
24192
24193 /*
24194 * Now, evaluate any generated expressions whose inputs come from the
24195 * new tuple. We assume these columns won't reference each other, so
24196 * that there's no ordering dependency.
24197 */
24199 insertslot, econtext);
24200
24201 /* Write the tuple out to the new relation. */
24202 table_tuple_insert(pc->partRel, insertslot, mycid,
24203 ti_options, pc->bistate);
24204
24205 ResetExprContext(econtext);
24206 }
24207
24209
24210 table_endscan(scan);
24211 UnregisterSnapshot(snapshot);
24212
24213 if (tuple_map)
24215
24217
24218 FreeExecutorState(estate);
24219
24222}
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 2529 of file tablecmds.c.

2530{
2531 switch (c)
2532 {
2533 case TYPSTORAGE_PLAIN:
2534 return "PLAIN";
2536 return "EXTERNAL";
2538 return "EXTENDED";
2539 case TYPSTORAGE_MAIN:
2540 return "MAIN";
2541 default:
2542 return "???";
2543 }
2544}
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 3600 of file tablecmds.c.

3602{
3603 Relation relation;
3605 ListCell *entry;
3606
3607 /*
3608 * sanity checks
3609 */
3611
3612 if (supers == NIL)
3613 return;
3614
3615 /*
3616 * Store INHERITS information in pg_inherits using direct ancestors only.
3617 * Also enter dependencies on the direct ancestors, and make sure they are
3618 * marked with relhassubclass = true.
3619 *
3620 * (Once upon a time, both direct and indirect ancestors were found here
3621 * and then entered into pg_ipl. Since that catalog doesn't exist
3622 * anymore, there's no need to look for indirect ancestors.)
3623 */
3625
3626 seqNumber = 1;
3627 foreach(entry, supers)
3628 {
3629 Oid parentOid = lfirst_oid(entry);
3630
3633 seqNumber++;
3634 }
3635
3636 table_close(relation, RowExclusiveLock);
3637}

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

3647{
3650
3651 /* store the pg_inherits row */
3653
3654 /*
3655 * Store a dependency too
3656 */
3658 parentobject.objectId = parentOid;
3659 parentobject.objectSubId = 0;
3661 childobject.objectId = relationId;
3662 childobject.objectSubId = 0;
3663
3666
3667 /*
3668 * Post creation hook of this inheritance. Since object_access_hook
3669 * doesn't take multiple object identifiers, we relay oid of parent
3670 * relation using auxiliary_id argument.
3671 */
3673 relationId, 0,
3674 parentOid, false);
3675
3676 /*
3677 * Mark the parent as having subclasses.
3678 */
3680}
void StoreSingleInheritance(Oid relationId, Oid parentOid, int32 seqNumber)
void SetRelationHasSubclass(Oid relationId, bool relhassubclass)
Definition tablecmds.c:3726

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

13866{
13867 ListCell *l;
13868 int attnum;
13869
13870 attnum = 0;
13871 foreach(l, colList)
13872 {
13873 char *attname = strVal(lfirst(l));
13876
13879 ereport(ERROR,
13881 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13882 attname)));
13884 if (attform->attnum < 0)
13885 ereport(ERROR,
13887 errmsg("system columns cannot be used in foreign keys")));
13888 if (attnum >= INDEX_MAX_KEYS)
13889 ereport(ERROR,
13891 errmsg("cannot have more than %d keys in a foreign key",
13892 INDEX_MAX_KEYS)));
13893 attnums[attnum] = attform->attnum;
13894 if (atttypids != NULL)
13895 atttypids[attnum] = attform->atttypid;
13896 if (attcollids != NULL)
13897 attcollids[attnum] = attform->attcollation;
13899 attnum++;
13900 }
13901
13902 return attnum;
13903}

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

14026{
14027 Oid indexoid = InvalidOid;
14028 bool found = false;
14029 bool found_deferrable = false;
14032 int i,
14033 j;
14034
14035 /*
14036 * Reject duplicate appearances of columns in the referenced-columns list.
14037 * Such a case is forbidden by the SQL standard, and even if we thought it
14038 * useful to allow it, there would be ambiguity about how to match the
14039 * list to unique indexes (in particular, it'd be unclear which index
14040 * opclass goes with which FK column).
14041 */
14042 for (i = 0; i < numattrs; i++)
14043 {
14044 for (j = i + 1; j < numattrs; j++)
14045 {
14046 if (attnums[i] == attnums[j])
14047 ereport(ERROR,
14049 errmsg("foreign key referenced-columns list must not contain duplicates")));
14050 }
14051 }
14052
14053 /*
14054 * Get the list of index OIDs for the table from the relcache, and look up
14055 * each one in the pg_index syscache, and match unique indexes to the list
14056 * of attnums we are given.
14057 */
14059
14060 foreach(indexoidscan, indexoidlist)
14061 {
14064
14065 indexoid = lfirst_oid(indexoidscan);
14068 elog(ERROR, "cache lookup failed for index %u", indexoid);
14070
14071 /*
14072 * Must have the right number of columns; must be unique (or if
14073 * temporal then exclusion instead) and not a partial index; forget it
14074 * if there are any expressions, too. Invalid indexes are out as well.
14075 */
14076 if (indexStruct->indnkeyatts == numattrs &&
14077 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
14081 {
14084
14085 /* Must get indclass the hard way */
14089
14090 /*
14091 * The given attnum list may match the index columns in any order.
14092 * Check for a match, and extract the appropriate opclasses while
14093 * we're at it.
14094 *
14095 * We know that attnums[] is duplicate-free per the test at the
14096 * start of this function, and we checked above that the number of
14097 * index columns agrees, so if we find a match for each attnums[]
14098 * entry then we must have a one-to-one match in some order.
14099 */
14100 for (i = 0; i < numattrs; i++)
14101 {
14102 found = false;
14103 for (j = 0; j < numattrs; j++)
14104 {
14105 if (attnums[i] == indexStruct->indkey.values[j])
14106 {
14107 opclasses[i] = indclass->values[j];
14108 found = true;
14109 break;
14110 }
14111 }
14112 if (!found)
14113 break;
14114 }
14115 /* The last attribute in the index must be the PERIOD FK part */
14116 if (found && with_period)
14117 {
14118 int16 periodattnum = attnums[numattrs - 1];
14119
14120 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
14121 }
14122
14123 /*
14124 * Refuse to use a deferrable unique/primary key. This is per SQL
14125 * spec, and there would be a lot of interesting semantic problems
14126 * if we tried to allow it.
14127 */
14128 if (found && !indexStruct->indimmediate)
14129 {
14130 /*
14131 * Remember that we found an otherwise matching index, so that
14132 * we can generate a more appropriate error message.
14133 */
14134 found_deferrable = true;
14135 found = false;
14136 }
14137
14138 /* We need to know whether the index has WITHOUT OVERLAPS */
14139 if (found)
14140 *pk_has_without_overlaps = indexStruct->indisexclusion;
14141 }
14143 if (found)
14144 break;
14145 }
14146
14147 if (!found)
14148 {
14149 if (found_deferrable)
14150 ereport(ERROR,
14152 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
14154 else
14155 ereport(ERROR,
14157 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
14159 }
14160
14162
14163 return indexoid;
14164}
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition heaptuple.c:456
Definition c.h:874

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

13923{
13930 int i;
13931
13932 /*
13933 * Get the list of index OIDs for the table from the relcache, and look up
13934 * each one in the pg_index syscache until we find one marked primary key
13935 * (hopefully there isn't more than one such). Insist it's valid, too.
13936 */
13937 *indexOid = InvalidOid;
13938
13940
13941 foreach(indexoidscan, indexoidlist)
13942 {
13943 Oid indexoid = lfirst_oid(indexoidscan);
13944
13947 elog(ERROR, "cache lookup failed for index %u", indexoid);
13949 if (indexStruct->indisprimary && indexStruct->indisvalid)
13950 {
13951 /*
13952 * Refuse to use a deferrable primary key. This is per SQL spec,
13953 * and there would be a lot of interesting semantic problems if we
13954 * tried to allow it.
13955 */
13956 if (!indexStruct->indimmediate)
13957 ereport(ERROR,
13959 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13961
13962 *indexOid = indexoid;
13963 break;
13964 }
13966 }
13967
13969
13970 /*
13971 * Check that we found it
13972 */
13973 if (!OidIsValid(*indexOid))
13974 ereport(ERROR,
13976 errmsg("there is no primary key for referenced table \"%s\"",
13978
13979 /* Must get indclass the hard way */
13983
13984 /*
13985 * Now build the list of PK attributes from the indkey definition (we
13986 * assume a primary key cannot have expressional elements)
13987 */
13988 *attnamelist = NIL;
13989 for (i = 0; i < indexStruct->indnkeyatts; i++)
13990 {
13991 int pkattno = indexStruct->indkey.values[i];
13992
13993 attnums[i] = pkattno;
13996 opclasses[i] = indclass->values[i];
13999 }
14000
14001 *pk_has_without_overlaps = indexStruct->indisexclusion;
14002
14004
14005 return i;
14006}
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 20459 of file tablecmds.c.

20460{
20462 ParseState *pstate;
20464 ListCell *l;
20465
20467
20468 newspec->strategy = partspec->strategy;
20469 newspec->partParams = NIL;
20470 newspec->location = partspec->location;
20471
20472 /* Check valid number of columns for strategy */
20473 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
20474 list_length(partspec->partParams) != 1)
20475 ereport(ERROR,
20477 errmsg("cannot use \"list\" partition strategy with more than one column")));
20478
20479 /*
20480 * Create a dummy ParseState and insert the target relation as its sole
20481 * rangetable entry. We need a ParseState for transformExpr.
20482 */
20483 pstate = make_parsestate(NULL);
20485 NULL, false, true);
20486 addNSItemToQuery(pstate, nsitem, true, true, true);
20487
20488 /* take care of any partition expressions */
20489 foreach(l, partspec->partParams)
20490 {
20492
20493 if (pelem->expr)
20494 {
20495 /* Copy, to avoid scribbling on the input */
20497
20498 /* Now do parse transformation of the expression */
20499 pelem->expr = transformExpr(pstate, pelem->expr,
20501
20502 /* we have to fix its collations too */
20503 assign_expr_collations(pstate, pelem->expr);
20504 }
20505
20506 newspec->partParams = lappend(newspec->partParams, pelem);
20507 }
20508
20509 return newspec;
20510}
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:919
List * partParams
Definition parsenodes.h:933
ParseLoc location
Definition parsenodes.h:934
PartitionStrategy strategy
Definition parsenodes.h:932

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

2507{
2508 /*
2509 * Don't allow truncate on temp tables of other backends ... their local
2510 * buffer manager is not going to cope.
2511 */
2512 if (RELATION_IS_OTHER_TEMP(rel))
2513 ereport(ERROR,
2515 errmsg("cannot truncate temporary tables of other sessions")));
2516
2517 /*
2518 * Also check for active uses of the relation in the current transaction,
2519 * including open scans and pending AFTER trigger events.
2520 */
2521 CheckTableNotInUse(rel, "TRUNCATE");
2522}

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

2489{
2490 char *relname = NameStr(reltuple->relname);
2492
2493 /* Permissions checks */
2495 if (aclresult != ACLCHECK_OK)
2497 relname);
2498}
#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 2435 of file tablecmds.c.

2436{
2437 char *relname = NameStr(reltuple->relname);
2438
2439 /*
2440 * Only allow truncate on regular tables, foreign tables using foreign
2441 * data wrappers supporting TRUNCATE and partitioned tables (although, the
2442 * latter are only being included here for the following checks; no
2443 * physical truncation will occur in their case.).
2444 */
2445 if (reltuple->relkind == RELKIND_FOREIGN_TABLE)
2446 {
2447 Oid serverid = GetForeignServerIdByRelId(relid);
2448 FdwRoutine *fdwroutine = GetFdwRoutineByServerId(serverid);
2449
2450 if (!fdwroutine->ExecForeignTruncate)
2451 ereport(ERROR,
2453 errmsg("cannot truncate foreign table \"%s\"",
2454 relname)));
2455 }
2456 else if (reltuple->relkind != RELKIND_RELATION &&
2458 ereport(ERROR,
2460 errmsg("\"%s\" is not a table", relname)));
2461
2462 /*
2463 * Most system catalogs can't be truncated at all, or at least not unless
2464 * allow_system_table_mods=on. As an exception, however, we allow
2465 * pg_largeobject and pg_largeobject_metadata to be truncated as part of
2466 * pg_upgrade, because we need to change its relfilenode to match the old
2467 * cluster, and allowing a TRUNCATE command to be executed is the easiest
2468 * way of doing that. We also allow TRUNCATE on the conflict log tables,
2469 * to permit users to manually prune conflict data to manage disk space.
2470 */
2473 && (!IsBinaryUpgrade ||
2474 (relid != LargeObjectRelationId &&
2476 ereport(ERROR,
2478 errmsg("permission denied: \"%s\" is a system catalog",
2479 relname)));
2480
2482}
bool IsBinaryUpgrade
Definition globals.c:123
#define InvokeObjectTruncateHook(objectId)

References allowSystemTableMods, ereport, errcode(), errmsg, ERROR, FdwRoutine::ExecForeignTruncate, fb(), GetFdwRoutineByServerId(), GetForeignServerIdByRelId(), InvokeObjectTruncateHook, IsBinaryUpgrade, IsConflictLogTableClass(), 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 11842 of file tablecmds.c.

11853{
11858
11862 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11864
11865 /*
11866 * Do some quick & easy initial checks. If any of these fail, we cannot
11867 * use this constraint.
11868 */
11869 if (fk->confrelid != parentConstr->confrelid || fk->nkeys != numfks)
11870 {
11872 return false;
11873 }
11874 for (int i = 0; i < numfks; i++)
11875 {
11876 if (fk->conkey[i] != mapped_conkey[i] ||
11877 fk->confkey[i] != confkey[i] ||
11878 fk->conpfeqop[i] != conpfeqop[i])
11879 {
11881 return false;
11882 }
11883 }
11884
11885 /* Looks good so far; perform more extensive checks. */
11888 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
11890
11891 /*
11892 * An error should be raised if the constraint enforceability is
11893 * different. Returning false without raising an error, as we do for other
11894 * attributes, could lead to a duplicate constraint with the same
11895 * enforceability as the parent. While this may be acceptable, it may not
11896 * be ideal. Therefore, it's better to raise an error and allow the user
11897 * to correct the enforceability before proceeding.
11898 */
11899 if (partConstr->conenforced != parentConstr->conenforced)
11900 ereport(ERROR,
11902 errmsg("constraint \"%s\" enforceability conflicts with constraint \"%s\" on relation \"%s\"",
11903 NameStr(parentConstr->conname),
11904 NameStr(partConstr->conname),
11906
11907 if (OidIsValid(partConstr->conparentid) ||
11908 partConstr->condeferrable != parentConstr->condeferrable ||
11909 partConstr->condeferred != parentConstr->condeferred ||
11910 partConstr->confupdtype != parentConstr->confupdtype ||
11911 partConstr->confdeltype != parentConstr->confdeltype ||
11912 partConstr->confmatchtype != parentConstr->confmatchtype)
11913 {
11916 return false;
11917 }
11918
11921
11922 /* Looks good! Attach this constraint. */
11926
11927 return true;
11928}
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 16620 of file tablecmds.c.

16621{
16622 HeapTuple tup;
16623 Datum adatum;
16624 ArrayType *arr;
16625 Oid *rawarr;
16626 int numkeys;
16627 int i;
16628
16629 Assert(con->contype == CONSTR_FOREIGN);
16630 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
16631
16633 if (!HeapTupleIsValid(tup)) /* should not happen */
16634 elog(ERROR, "cache lookup failed for constraint %u", oldId);
16635
16638 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
16639 numkeys = ARR_DIMS(arr)[0];
16640 /* test follows the one in ri_FetchConstraintInfo() */
16641 if (ARR_NDIM(arr) != 1 ||
16642 ARR_HASNULL(arr) ||
16643 ARR_ELEMTYPE(arr) != OIDOID)
16644 elog(ERROR, "conpfeqop is not a 1-D Oid array");
16645 rawarr = (Oid *) ARR_DATA_PTR(arr);
16646
16647 /* stash a List of the operator Oids in our Constraint node */
16648 for (i = 0; i < numkeys; i++)
16650
16652}
#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 16591 of file tablecmds.c.

16592{
16594 stmt->accessMethod,
16595 stmt->indexParams,
16596 stmt->excludeOpNames,
16597 stmt->iswithoutoverlaps))
16598 {
16600
16601 /* If it's a partitioned index, there is no storage to share. */
16602 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
16603 {
16604 stmt->oldNumber = irel->rd_locator.relNumber;
16605 stmt->oldCreateSubid = irel->rd_createSubid;
16606 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
16607 }
16608 index_close(irel, NoLock);
16609 }
16610}
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition indexcmds.c:189

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

10797{
10798 int numcolsout = 0;
10799
10800 for (int i = 0; i < numfksetcols; i++)
10801 {
10803 bool seen = false;
10804
10805 /* Make sure it's in fkattnums[] */
10806 for (int j = 0; j < numfks; j++)
10807 {
10808 if (fkattnums[j] == setcol_attnum)
10809 {
10810 seen = true;
10811 break;
10812 }
10813 }
10814
10815 if (!seen)
10816 {
10817 char *col = strVal(list_nth(fksetcols, i));
10818
10819 ereport(ERROR,
10821 errmsg("column \"%s\" referenced in ON DELETE SET action must be part of foreign key", col)));
10822 }
10823
10824 /* Now check for dups */
10825 seen = false;
10826 for (int j = 0; j < numcolsout; j++)
10827 {
10829 {
10830 seen = true;
10831 break;
10832 }
10833 }
10834 if (!seen)
10836 }
10837 return numcolsout;
10838}

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

14237{
14238 TupleTableSlot *slot;
14239 TableScanDesc scan;
14240 Trigger trig = {0};
14241 Snapshot snapshot;
14244
14246 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
14247
14248 /*
14249 * Build a trigger call structure; we'll need it either way.
14250 */
14251 trig.tgoid = InvalidOid;
14252 trig.tgname = conname;
14253 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
14254 trig.tgisinternal = true;
14255 trig.tgconstrrelid = RelationGetRelid(pkrel);
14256 trig.tgconstrindid = pkindOid;
14257 trig.tgconstraint = constraintOid;
14258 trig.tgdeferrable = false;
14259 trig.tginitdeferred = false;
14260 /* we needn't fill in remaining fields */
14261
14262 /*
14263 * See if we can do it with a single LEFT JOIN query. A false result
14264 * indicates we must proceed with the fire-the-trigger method. We can't do
14265 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
14266 * left joins.
14267 */
14268 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
14269 return;
14270
14271 /*
14272 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
14273 * if that tuple had just been inserted. If any of those fail, it should
14274 * ereport(ERROR) and that's that.
14275 */
14276 snapshot = RegisterSnapshot(GetLatestSnapshot());
14277 slot = table_slot_create(rel, NULL);
14278 scan = table_beginscan(rel, snapshot, 0, NULL,
14279 SO_NONE);
14281 "validateForeignKeyConstraint",
14284
14285 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
14286 {
14287 LOCAL_FCINFO(fcinfo, 0);
14288 TriggerData trigdata = {0};
14289
14291
14292 /*
14293 * Make a call to the trigger function
14294 *
14295 * No parameters are passed, but we do set a context
14296 */
14297 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
14298
14299 /*
14300 * We assume RI_FKey_check_ins won't look at flinfo...
14301 */
14302 trigdata.type = T_TriggerData;
14304 trigdata.tg_relation = rel;
14305 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
14306 trigdata.tg_trigslot = slot;
14307 trigdata.tg_trigger = &trig;
14308
14309 fcinfo->context = (Node *) &trigdata;
14310
14311 RI_FKey_check_ins(fcinfo);
14312
14314 }
14315
14318 table_endscan(scan);
14319 UnregisterSnapshot(snapshot);
14321}
#define MemSet(start, val, len)
Definition c.h:1147
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 22564 of file tablecmds.c.

22565{
22567 SysScanDesc scan;
22569 int tuples = 0;
22571 bool updated = false;
22572
22573 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
22574
22575 /*
22576 * Scan pg_inherits for this parent index. Count each valid index we find
22577 * (verifying the pg_index entry for each), and if we reach the total
22578 * amount we expect, we can mark this parent index as valid.
22579 */
22585 NULL, 1, &key);
22586 while ((inhTup = systable_getnext(scan)) != NULL)
22587 {
22591
22593 ObjectIdGetDatum(inhForm->inhrelid));
22595 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
22597 if (indexForm->indisvalid)
22598 tuples += 1;
22600 }
22601
22602 /* Done with pg_inherits */
22603 systable_endscan(scan);
22605
22606 /*
22607 * If we found as many inherited indexes as the partitioned table has
22608 * partitions, we're good; update pg_index to set indisvalid.
22609 */
22610 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
22611 {
22615
22620 elog(ERROR, "cache lookup failed for index %u",
22623
22624 indexForm->indisvalid = true;
22625 updated = true;
22626
22628
22631 }
22632
22633 /*
22634 * If this index is in turn a partition of a larger index, validating it
22635 * might cause the parent to become valid also. Try that.
22636 */
22637 if (updated && partedIdx->rd_rel->relispartition)
22638 {
22641 Relation parentIdx,
22642 parentTbl;
22643
22644 /* make sure we see the validation we just did */
22646
22651 Assert(!parentIdx->rd_index->indisvalid);
22652
22653 validatePartitionedIndex(parentIdx, parentTbl);
22654
22657 }
22658}

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

9714{
9716
9717 if (conForm->contype != CONSTRAINT_NOTNULL)
9718 elog(ERROR, "constraint %u is not a not-null constraint", conForm->oid);
9719
9720 /* a NO INHERIT constraint is no good */
9721 if (conForm->connoinherit)
9722 ereport(ERROR,
9724 errmsg("cannot create primary key on column \"%s\"", colname),
9725 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9726 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9727 NameStr(conForm->conname), colname,
9728 get_rel_name(conForm->conrelid), "NO INHERIT"),
9729 errhint("You might need to make the existing constraint inheritable using %s.",
9730 "ALTER TABLE ... ALTER CONSTRAINT ... INHERIT"));
9731
9732 /* an unvalidated constraint is no good */
9733 if (!conForm->convalidated)
9734 ereport(ERROR,
9736 errmsg("cannot create primary key on column \"%s\"", colname),
9737 /*- translator: fourth %s is a constraint characteristic such as NOT VALID */
9738 errdetail("The constraint \"%s\" on column \"%s\" of table \"%s\", marked %s, is incompatible with a primary key.",
9739 NameStr(conForm->conname), colname,
9740 get_rel_name(conForm->conrelid), "NOT VALID"),
9741 errhint("You might need to validate it using %s.",
9742 "ALTER TABLE ... VALIDATE CONSTRAINT"));
9743}

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

22667{
22668 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
22669 {
22671 iinfo->ii_IndexAttrNumbers[i] - 1);
22672
22673 if (!att->attnotnull)
22674 ereport(ERROR,
22676 errmsg("invalid primary key definition"),
22677 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
22678 NameStr(att->attname),
22680 }
22681}

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

Referenced by ATExecAttachPartitionIdx().

Variable Documentation

◆ dropmsgstringarray

const struct dropmsgstrings dropmsgstringarray[]
static

Definition at line 259 of file tablecmds.c.

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

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits