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

7815{
7817 referenced;
7818
7819 /* We know the default collation is pinned, so don't bother recording it */
7821 {
7823 myself.objectId = relid;
7824 myself.objectSubId = attnum;
7826 referenced.objectId = collid;
7827 referenced.objectSubId = 0;
7829 }
7830}
#define OidIsValid(objectId)
Definition c.h:914
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 7796 of file tablecmds.c.

7797{
7799 referenced;
7800
7802 myself.objectId = relid;
7803 myself.objectSubId = attnum;
7804 referenced.classId = TypeRelationId;
7805 referenced.objectId = typid;
7806 referenced.objectSubId = 0;
7808}

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

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

10994{
10997
11000
11001 /*
11002 * Create action triggers to enforce the constraint, or skip them if the
11003 * constraint is NOT ENFORCED.
11004 */
11005 if (fkconstraint->is_enforced)
11009 parentConstr, indexOid,
11012
11013 /*
11014 * If the referenced table is partitioned, recurse on ourselves to handle
11015 * each partition. We need one pg_constraint row created for each
11016 * partition in addition to the pg_constraint row for the parent table.
11017 */
11018 if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11019 {
11021
11022 for (int i = 0; i < pd->nparts; i++)
11023 {
11024 Relation partRel;
11025 AttrMap *map;
11028 ObjectAddress address;
11029
11030 /* XXX would it be better to acquire these locks beforehand? */
11031 partRel = table_open(pd->oids[i], ShareRowExclusiveLock);
11032
11033 /*
11034 * Map the attribute numbers in the referenced side of the FK
11035 * definition to match the partition's column layout.
11036 */
11039 false);
11040 if (map)
11041 {
11043 for (int j = 0; j < numfks; j++)
11044 mapped_pkattnum[j] = map->attnums[pkattnum[j] - 1];
11045 }
11046 else
11048
11049 /* Determine the index to use at this level */
11050 partIndexId = index_get_partition(partRel, indexOid);
11051 if (!OidIsValid(partIndexId))
11052 elog(ERROR, "index for %u not found in partition %s",
11053 indexOid, RelationGetRelationName(partRel));
11054
11055 /* Create entry at this level ... */
11057 fkconstraint->conname, fkconstraint, rel,
11058 partRel, partIndexId, parentConstr,
11062 fkdelsetcols, true, with_period);
11063 /* ... and recurse to our children */
11065 partIndexId, address.objectId, numfks,
11071 with_period);
11072
11073 /* Done -- clean up (but keep the lock) */
11074 table_close(partRel, NoLock);
11075 if (map)
11076 {
11078 free_attrmap(map);
11079 }
11080 }
11081 }
11082}
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 11123 of file tablecmds.c.

11131{
11134
11138
11139 if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
11140 ereport(ERROR,
11142 errmsg("foreign key constraints are not supported on foreign tables")));
11143
11144 /*
11145 * Add check triggers if the constraint is ENFORCED, and if needed,
11146 * schedule them to be checked in Phase 3.
11147 *
11148 * If the relation is partitioned, drill down to do it to its partitions.
11149 */
11150 if (fkconstraint->is_enforced)
11155 indexOid,
11158
11159 if (rel->rd_rel->relkind == RELKIND_RELATION)
11160 {
11161 /*
11162 * Tell Phase 3 to check that the constraint is satisfied by existing
11163 * rows. We can skip this during table creation, when constraint is
11164 * specified as NOT ENFORCED, or when requested explicitly by
11165 * specifying NOT VALID in an ADD FOREIGN KEY command, and when we're
11166 * recreating a constraint following a SET DATA TYPE operation that
11167 * did not impugn its validity.
11168 */
11169 if (wqueue && !old_check_ok && !fkconstraint->skip_validation &&
11170 fkconstraint->is_enforced)
11171 {
11173 AlteredTableInfo *tab;
11174
11175 tab = ATGetQueueEntry(wqueue, rel);
11176
11179 newcon->contype = CONSTR_FOREIGN;
11180 newcon->refrelid = RelationGetRelid(pkrel);
11181 newcon->refindid = indexOid;
11182 newcon->conid = parentConstr;
11183 newcon->conwithperiod = fkconstraint->fk_with_period;
11184 newcon->qual = (Node *) fkconstraint;
11185
11186 tab->constraints = lappend(tab->constraints, newcon);
11187 }
11188 }
11189 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
11190 {
11193
11194 /*
11195 * Triggers of the foreign keys will be manipulated a bunch of times
11196 * in the loop below. To avoid repeatedly opening/closing the trigger
11197 * catalog relation, we open it here and pass it to the subroutines
11198 * called below.
11199 */
11201
11202 /*
11203 * Recurse to take appropriate action on each partition; either we
11204 * find an existing constraint to reparent to ours, or we create a new
11205 * one.
11206 */
11207 for (int i = 0; i < pd->nparts; i++)
11208 {
11209 Relation partition = table_open(pd->oids[i], lockmode);
11210 List *partFKs;
11211 AttrMap *attmap;
11213 bool attached;
11214 ObjectAddress address;
11215
11217
11219 RelationGetDescr(rel),
11220 false);
11221 for (int j = 0; j < numfks; j++)
11222 mapped_fkattnum[j] = attmap->attnums[fkattnum[j] - 1];
11223
11224 /* Check whether an existing constraint can be repurposed */
11226 attached = false;
11228 {
11230 fk,
11231 partition,
11233 numfks,
11235 pkattnum,
11239 trigrel))
11240 {
11241 attached = true;
11242 break;
11243 }
11244 }
11245 if (attached)
11246 {
11248 continue;
11249 }
11250
11251 /*
11252 * No luck finding a good constraint to reuse; create our own.
11253 */
11255 fkconstraint->conname, fkconstraint,
11256 partition, pkrel, indexOid, parentConstr,
11261 with_period);
11262
11263 /* call ourselves to finalize the creation and we're done */
11265 indexOid,
11266 address.objectId,
11267 numfks,
11268 pkattnum,
11276 lockmode,
11279 with_period);
11280
11282 }
11283
11285 }
11286}
AttrMap * build_attrmap_by_name(TupleDesc indesc, TupleDesc outdesc, bool missing_ok)
Definition attmap.c:175
#define palloc0_object(type)
Definition fe_memutils.h:90
List * lappend(List *list, void *datum)
Definition list.c:339
#define RowExclusiveLock
Definition lockdefs.h:38
char * get_constraint_name(Oid conoid)
Definition lsyscache.c:1299
#define copyObject(obj)
Definition nodes.h: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:4741
Definition pg_list.h:54
Definition nodes.h:133
static AlteredTableInfo * ATGetQueueEntry(List **wqueue, Relation rel)
Definition tablecmds.c:6655
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:4513
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 6689 of file tablecmds.c.

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

12858{
12861 ScanKeyData skey[3];
12862
12863 ScanKeyInit(&skey[0],
12866 ObjectIdGetDatum(conrelid));
12867 ScanKeyInit(&skey[1],
12871 ScanKeyInit(&skey[2],
12874 CStringGetDatum(cmdcon->conname));
12875
12877 NULL, 3, skey);
12878
12880 ereport(ERROR,
12882 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
12883 cmdcon->conname, get_rel_name(conrelid)));
12884
12886 recurse, recursing, changing_conids,
12887 lockmode);
12888
12890}
void systable_endscan(SysScanDesc sysscan)
Definition genam.c:604
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition genam.c:515
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition genam.c:388
#define HeapTupleIsValid(tuple)
Definition htup.h:78
char * get_rel_name(Oid relid)
Definition lsyscache.c:2234
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
static Datum CStringGetDatum(const char *X)
Definition postgres.h:383
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition scankey.c:76
#define BTEqualStrategyNumber
Definition stratnum.h:31
static bool ATExecAlterCheckConstrEnforceability(List **wqueue, ATAlterConstraint *cmdcon, Relation conrel, HeapTuple contuple, bool recurse, bool recursing, 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 13283 of file tablecmds.c.

13287{
13289 Oid conoid;
13290 ScanKeyData pkey;
13293
13295 conoid = currcon->oid;
13296
13297 ScanKeyInit(&pkey,
13300 ObjectIdGetDatum(conoid));
13301
13303 true, NULL, 1, &pkey);
13304
13306 {
13309
13310 childrel = table_open(childcon->conrelid, lockmode);
13311
13313 childtup, recurse, otherrelids, lockmode);
13315 }
13316
13318}
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 13165 of file tablecmds.c.

13168{
13172
13176 ObjectIdGetDatum(conoid));
13178 NULL, 1, &tgkey);
13180 {
13184
13185 /*
13186 * Remember OIDs of other relation(s) involved in FK constraint.
13187 * (Note: it's likely that we could skip forcing a relcache inval for
13188 * other rels that don't have a trigger whose properties change, but
13189 * let's be conservative.)
13190 */
13191 if (tgform->tgrelid != RelationGetRelid(rel))
13193 tgform->tgrelid);
13194
13195 /*
13196 * Update enable status and deferrability of RI_FKey_noaction_del,
13197 * RI_FKey_noaction_upd, RI_FKey_check_ins and RI_FKey_check_upd
13198 * triggers, but not others; see createForeignKeyActionTriggers and
13199 * CreateFKCheckTrigger.
13200 */
13201 if (tgform->tgfoid != F_RI_FKEY_NOACTION_DEL &&
13202 tgform->tgfoid != F_RI_FKEY_NOACTION_UPD &&
13203 tgform->tgfoid != F_RI_FKEY_CHECK_INS &&
13204 tgform->tgfoid != F_RI_FKEY_CHECK_UPD)
13205 continue;
13206
13209
13210 copy_tg->tgdeferrable = deferrable;
13211 copy_tg->tginitdeferred = initdeferred;
13213
13215
13217 }
13218
13220}
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 13325 of file tablecmds.c.

13327{
13330
13331 Assert(cmdcon->alterEnforceability || cmdcon->alterDeferrability ||
13332 cmdcon->alterInheritability);
13333
13336
13337 if (cmdcon->alterEnforceability)
13338 {
13339 copy_con->conenforced = cmdcon->is_enforced;
13340
13341 /*
13342 * NB: The convalidated status is irrelevant when the constraint is
13343 * set to NOT ENFORCED, but for consistency, it should still be set
13344 * appropriately. Similarly, if the constraint is later changed to
13345 * ENFORCED, validation will be performed during phase 3, so it makes
13346 * sense to mark it as valid in that case.
13347 */
13348 copy_con->convalidated = cmdcon->is_enforced;
13349 }
13350 if (cmdcon->alterDeferrability)
13351 {
13352 copy_con->condeferrable = cmdcon->deferrable;
13353 copy_con->condeferred = cmdcon->initdeferred;
13354 }
13355 if (cmdcon->alterInheritability)
13356 copy_con->connoinherit = cmdcon->noinherit;
13357
13360
13361 /* Make new constraint flags visible to others */
13363
13365}
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 13234 of file tablecmds.c.

13242{
13244 Oid conoid;
13245 ScanKeyData pkey;
13248
13250 conoid = currcon->oid;
13251
13252 ScanKeyInit(&pkey,
13255 ObjectIdGetDatum(conoid));
13256
13258 true, NULL, 1, &pkey);
13259
13262 pkrelid, childtup, lockmode,
13267
13269}
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 19597 of file tablecmds.c.

19599{
19600 List *indexList;
19601 ListCell *l;
19602
19604
19605 foreach(l, indexList)
19606 {
19607 Oid indexOid = lfirst_oid(l);
19609
19611 thisobj.objectId = indexOid;
19612 thisobj.objectSubId = 0;
19613
19614 /*
19615 * Note: currently, the index will not have its own dependency on the
19616 * namespace, so we don't need to do changeDependencyFor(). There's no
19617 * row type in pg_type, either.
19618 *
19619 * XXX this objsMoved test may be pointless -- surely we have a single
19620 * dependency link from a relation to each index?
19621 */
19623 {
19626 false, objsMoved);
19628 }
19629 }
19630
19632}
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:4846
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 19520 of file tablecmds.c.

19524{
19528 bool already_done = false;
19529
19530 /* no rel lock for relkind=c so use LOCKTAG_TUPLE */
19533 elog(ERROR, "cache lookup failed for relation %u", relOid);
19535
19536 Assert(classForm->relnamespace == oldNspOid);
19537
19538 thisobj.classId = RelationRelationId;
19539 thisobj.objectId = relOid;
19540 thisobj.objectSubId = 0;
19541
19542 /*
19543 * If the object has already been moved, don't move it again. If it's
19544 * already in the right place, don't move it, but still fire the object
19545 * access hook.
19546 */
19548 if (!already_done && oldNspOid != newNspOid)
19549 {
19550 ItemPointerData otid = classTup->t_self;
19551
19552 /* check for duplicate name (more friendly than unique-index failure) */
19553 if (get_relname_relid(NameStr(classForm->relname),
19555 ereport(ERROR,
19557 errmsg("relation \"%s\" already exists in schema \"%s\"",
19558 NameStr(classForm->relname),
19560
19561 /* classTup is a copy, so OK to scribble on */
19562 classForm->relnamespace = newNspOid;
19563
19566
19567
19568 /* Update dependency on schema if caller said so */
19569 if (hasDependEntry &&
19571 relOid,
19573 oldNspOid,
19574 newNspOid) != 1)
19575 elog(ERROR, "could not change schema dependency for relation \"%s\"",
19576 NameStr(classForm->relname));
19577 }
19578 else
19580 if (!already_done)
19581 {
19583
19585 }
19586
19588}
#define NameStr(name)
Definition c.h:891
void UnlockTuple(Relation relation, const ItemPointerData *tid, LOCKMODE lockmode)
Definition lmgr.c:601
#define InplaceUpdateTupleLock
Definition lockdefs.h:48
char * get_namespace_name(Oid nspid)
Definition lsyscache.c:3674
Oid get_relname_relid(const char *relname, Oid relnamespace)
Definition lsyscache.c:2191
FormData_pg_class * Form_pg_class
Definition pg_class.h:160
long changeDependencyFor(Oid classId, Oid objectId, Oid refClassId, Oid oldRefObjectId, Oid newRefObjectId)
Definition pg_depend.c:470
HeapTuple SearchSysCacheLockedCopy1(SysCacheIdentifier cacheId, Datum key1)
Definition syscache.c:400

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

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

◆ AlterSeqNamespaces()

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

Definition at line 19642 of file tablecmds.c.

19645{
19647 SysScanDesc scan;
19648 ScanKeyData key[2];
19649 HeapTuple tup;
19650
19651 /*
19652 * SERIAL sequences are those having an auto dependency on one of the
19653 * table's columns (we don't care *which* column, exactly).
19654 */
19656
19657 ScanKeyInit(&key[0],
19661 ScanKeyInit(&key[1],
19665 /* we leave refobjsubid unspecified */
19666
19668 NULL, 2, key);
19669
19670 while (HeapTupleIsValid(tup = systable_getnext(scan)))
19671 {
19674
19675 /* skip dependencies other than auto dependencies on columns */
19676 if (depForm->refobjsubid == 0 ||
19677 depForm->classid != RelationRelationId ||
19678 depForm->objsubid != 0 ||
19679 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
19680 continue;
19681
19682 /* Use relation_open just in case it's an index */
19683 seqRel = relation_open(depForm->objid, lockmode);
19684
19685 /* skip non-sequence relations */
19686 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
19687 {
19688 /* No need to keep the lock */
19689 relation_close(seqRel, lockmode);
19690 continue;
19691 }
19692
19693 /* Fix the pg_class and pg_depend entries */
19696 true, objsMoved);
19697
19698 /*
19699 * Sequences used to have entries in pg_type, but no longer do. If we
19700 * ever re-instate that, we'll need to move the pg_type entry to the
19701 * new namespace, too (using AlterTypeNamespaceInternal).
19702 */
19704
19705 /* Now we can close it. Keep the lock till end of transaction. */
19707 }
19708
19709 systable_endscan(scan);
19710
19712}
@ 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 4598 of file tablecmds.c.

4600{
4601 Relation rel;
4602
4603 /* Caller is required to provide an adequate lock. */
4604 rel = relation_open(context->relid, NoLock);
4605
4607
4608 ATController(stmt, rel, stmt->cmds, stmt->relation->inh, lockmode, context);
4609}
#define stmt
static void ATController(AlterTableStmt *parsetree, Relation rel, List *cmds, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:4939

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

Referenced by ProcessUtilitySlow().

◆ AlterTableGetLockLevel()

LOCKMODE AlterTableGetLockLevel ( List cmds)

Definition at line 4672 of file tablecmds.c.

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

4628{
4629 Relation rel;
4630 LOCKMODE lockmode = AlterTableGetLockLevel(cmds);
4631
4632 rel = relation_open(relid, lockmode);
4633
4635
4636 ATController(NULL, rel, cmds, recurse, lockmode, NULL);
4637}
void EventTriggerAlterTableRelid(Oid objectId)
LOCKMODE AlterTableGetLockLevel(List *cmds)
Definition tablecmds.c:4672

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

Referenced by AlterTableMoveAll(), and DefineVirtualRelation().

◆ AlterTableLookupRelation()

Oid AlterTableLookupRelation ( AlterTableStmt stmt,
LOCKMODE  lockmode 
)

Definition at line 4539 of file tablecmds.c.

4540{
4541 return RangeVarGetRelidExtended(stmt->relation, lockmode,
4542 stmt->missing_ok ? RVR_MISSING_OK : 0,
4544 stmt);
4545}
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 17460 of file tablecmds.c.

17461{
17462 List *relations = NIL;
17463 ListCell *l;
17464 ScanKeyData key[1];
17465 Relation rel;
17466 TableScanDesc scan;
17467 HeapTuple tuple;
17470 List *role_oids = roleSpecsToIds(stmt->roles);
17471
17472 /* Ensure we were not asked to move something we can't */
17473 if (stmt->objtype != OBJECT_TABLE && stmt->objtype != OBJECT_INDEX &&
17474 stmt->objtype != OBJECT_MATVIEW)
17475 ereport(ERROR,
17477 errmsg("only tables, indexes, and materialized views exist in tablespaces")));
17478
17479 /* Get the orig and new tablespace OIDs */
17480 orig_tablespaceoid = get_tablespace_oid(stmt->orig_tablespacename, false);
17481 new_tablespaceoid = get_tablespace_oid(stmt->new_tablespacename, false);
17482
17483 /* Can't move shared relations in to or out of pg_global */
17484 /* This is also checked by ATExecSetTableSpace, but nice to stop earlier */
17487 ereport(ERROR,
17489 errmsg("cannot move relations in to or out of pg_global tablespace")));
17490
17491 /*
17492 * Must have CREATE rights on the new tablespace, unless it is the
17493 * database default tablespace (which all users implicitly have CREATE
17494 * rights on).
17495 */
17497 {
17499
17501 ACL_CREATE);
17502 if (aclresult != ACLCHECK_OK)
17505 }
17506
17507 /*
17508 * Now that the checks are done, check if we should set either to
17509 * InvalidOid because it is our database's default tablespace.
17510 */
17513
17516
17517 /* no-op */
17519 return new_tablespaceoid;
17520
17521 /*
17522 * Walk the list of objects in the tablespace and move them. This will
17523 * only find objects in our database, of course.
17524 */
17525 ScanKeyInit(&key[0],
17529
17531 scan = table_beginscan_catalog(rel, 1, key);
17532 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
17533 {
17535 Oid relOid = relForm->oid;
17536
17537 /*
17538 * Do not move objects in pg_catalog as part of this, if an admin
17539 * really wishes to do so, they can issue the individual ALTER
17540 * commands directly.
17541 *
17542 * Also, explicitly avoid any shared tables, temp tables, or TOAST
17543 * (TOAST will be moved with the main table).
17544 */
17545 if (IsCatalogNamespace(relForm->relnamespace) ||
17546 relForm->relisshared ||
17547 isAnyTempNamespace(relForm->relnamespace) ||
17548 IsToastNamespace(relForm->relnamespace))
17549 continue;
17550
17551 /* Only move the object type requested */
17552 if ((stmt->objtype == OBJECT_TABLE &&
17553 relForm->relkind != RELKIND_RELATION &&
17554 relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
17555 (stmt->objtype == OBJECT_INDEX &&
17556 relForm->relkind != RELKIND_INDEX &&
17557 relForm->relkind != RELKIND_PARTITIONED_INDEX) ||
17558 (stmt->objtype == OBJECT_MATVIEW &&
17559 relForm->relkind != RELKIND_MATVIEW))
17560 continue;
17561
17562 /* Check if we are only moving objects owned by certain roles */
17563 if (role_oids != NIL && !list_member_oid(role_oids, relForm->relowner))
17564 continue;
17565
17566 /*
17567 * Handle permissions-checking here since we are locking the tables
17568 * and also to avoid doing a bunch of work only to fail part-way. Note
17569 * that permissions will also be checked by AlterTableInternal().
17570 *
17571 * Caller must be considered an owner on the table to move it.
17572 */
17575 NameStr(relForm->relname));
17576
17577 if (stmt->nowait &&
17579 ereport(ERROR,
17581 errmsg("aborting because lock on relation \"%s.%s\" is not available",
17582 get_namespace_name(relForm->relnamespace),
17583 NameStr(relForm->relname))));
17584 else
17586
17587 /* Add to our list of objects to move */
17588 relations = lappend_oid(relations, relOid);
17589 }
17590
17591 table_endscan(scan);
17593
17594 if (relations == NIL)
17597 errmsg("no matching relations in tablespace \"%s\" found",
17598 orig_tablespaceoid == InvalidOid ? "(database default)" :
17600
17601 /* Everything is locked, loop through and move all of the relations. */
17602 foreach(l, relations)
17603 {
17604 List *cmds = NIL;
17606
17608 cmd->name = stmt->new_tablespacename;
17609
17610 cmds = lappend(cmds, cmd);
17611
17613 /* OID is set by AlterTableInternal */
17614 AlterTableInternal(lfirst_oid(l), cmds, false);
17616 }
17617
17618 return new_tablespaceoid;
17619}
AclResult
Definition acl.h:183
@ ACLCHECK_OK
Definition acl.h:184
@ ACLCHECK_NOT_OWNER
Definition acl.h:186
void aclcheck_error(AclResult aclerr, ObjectType objtype, const char *objectname)
Definition aclchk.c:2672
AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
Definition aclchk.c:3880
bool object_ownercheck(Oid classid, Oid objectid, Oid roleid)
Definition aclchk.c:4134
char * get_tablespace_name(Oid spc_oid)
Oid get_tablespace_oid(const char *tablespacename, bool missing_ok)
bool IsToastNamespace(Oid namespaceId)
Definition catalog.c:261
bool IsCatalogNamespace(Oid namespaceId)
Definition catalog.c:243
#define NOTICE
Definition elog.h:36
void EventTriggerAlterTableStart(const Node *parsetree)
void EventTriggerAlterTableEnd(void)
Oid MyDatabaseTableSpace
Definition globals.c:98
HeapTuple heap_getnext(TableScanDesc sscan, ScanDirection direction)
Definition heapam.c:1435
List * lappend_oid(List *list, Oid datum)
Definition list.c:375
bool list_member_oid(const List *list, Oid datum)
Definition list.c:722
bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:151
void LockRelationOid(Oid relid, LOCKMODE lockmode)
Definition lmgr.c:107
char get_rel_relkind(Oid relid)
Definition lsyscache.c:2309
Oid GetUserId(void)
Definition miscinit.c:470
bool isAnyTempNamespace(Oid namespaceId)
Definition namespace.c:3759
#define makeNode(_type_)
Definition nodes.h: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:4627
List * roleSpecsToIds(List *memberNames)
Definition user.c:1665

References AccessExclusiveLock, AccessShareLock, ACL_CREATE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, AlterTableInternal(), AT_SetTableSpace, BTEqualStrategyNumber, ConditionalLockRelationOid(), ereport, errcode(), errmsg, ERROR, EventTriggerAlterTableEnd(), EventTriggerAlterTableStart(), fb(), ForwardScanDirection, get_namespace_name(), get_rel_relkind(), get_relkind_objtype(), get_tablespace_name(), get_tablespace_oid(), GETSTRUCT(), GetUserId(), heap_getnext(), InvalidOid, isAnyTempNamespace(), IsCatalogNamespace(), IsToastNamespace(), lappend(), lappend_oid(), lfirst_oid, list_member_oid(), LockRelationOid(), makeNode, MyDatabaseTableSpace, AlterTableCmd::name, NameStr, NIL, NOTICE, object_aclcheck(), OBJECT_INDEX, OBJECT_MATVIEW, object_ownercheck(), OBJECT_TABLE, OBJECT_TABLESPACE, ObjectIdGetDatum(), OidIsValid, roleSpecsToIds(), ScanKeyInit(), stmt, AlterTableCmd::subtype, table_beginscan_catalog(), table_close(), table_endscan(), and table_open().

Referenced by ProcessUtilitySlow().

◆ AlterTableNamespace()

ObjectAddress AlterTableNamespace ( AlterObjectSchemaStmt stmt,
Oid oldschema 
)

Definition at line 19412 of file tablecmds.c.

19413{
19414 Relation rel;
19415 Oid relid;
19416 Oid oldNspOid;
19417 Oid nspOid;
19418 RangeVar *newrv;
19421
19423 stmt->missing_ok ? RVR_MISSING_OK : 0,
19425 stmt);
19426
19427 if (!OidIsValid(relid))
19428 {
19430 (errmsg("relation \"%s\" does not exist, skipping",
19431 stmt->relation->relname)));
19432 return InvalidObjectAddress;
19433 }
19434
19435 rel = relation_open(relid, NoLock);
19436
19438
19439 /* If it's an owned sequence, disallow moving it by itself. */
19440 if (rel->rd_rel->relkind == RELKIND_SEQUENCE)
19441 {
19442 Oid tableId;
19443 int32 colId;
19444
19445 if (sequenceIsOwned(relid, DEPENDENCY_AUTO, &tableId, &colId) ||
19447 ereport(ERROR,
19449 errmsg("cannot move an owned sequence into another schema"),
19450 errdetail("Sequence \"%s\" is linked to table \"%s\".",
19453 }
19454
19455 /* Get and lock schema OID and check its permissions. */
19456 newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1);
19458
19459 /* common checks on switching namespaces */
19461
19465
19467
19468 if (oldschema)
19470
19471 /* close rel, but keep lock until commit */
19472 relation_close(rel, NoLock);
19473
19474 return myself;
19475}
int32_t int32
Definition c.h:676
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 19483 of file tablecmds.c.

19485{
19487
19488 Assert(objsMoved != NULL);
19489
19490 /* OK, modify the pg_class row and pg_depend entry */
19492
19494 nspOid, true, objsMoved);
19495
19496 /* Fix the table's row type too, if it has one */
19497 if (OidIsValid(rel->rd_rel->reltype))
19499 false, /* isImplicitArray */
19500 false, /* ignoreDependent */
19501 false, /* errorOnTableType */
19502 objsMoved);
19503
19504 /* Fix other dependent stuff */
19509 false, objsMoved);
19510
19512}
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:4196

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

23425{
23426 Relation partRel;
23427 List *indexList;
23428
23429 if (extDepState == NIL)
23430 return;
23431
23432 /*
23433 * Use NoLock since the caller already holds AccessExclusiveLock on the
23434 * new partition.
23435 */
23436 partRel = table_open(newPartOid, NoLock);
23438
23439 foreach_oid(indexOid, indexList)
23440 {
23442
23443 if (!get_rel_relispartition(indexOid))
23444 continue;
23445
23446 parentIdxOid = get_partition_parent(indexOid, true);
23448 continue;
23449
23451 {
23453
23454 if (entry->parentIndexOid > parentIdxOid)
23455 break;
23456 if (entry->parentIndexOid < parentIdxOid)
23457 continue;
23458
23460
23461 foreach_oid(extOid, entry->extensionOids)
23462 {
23464
23468 }
23469 break;
23470 }
23471 }
23472
23474 table_close(partRel, NoLock);
23475}
@ DEPENDENCY_AUTO_EXTENSION
Definition dependency.h:39
bool get_rel_relispartition(Oid relid)
Definition lsyscache.c:2333
Oid get_partition_parent(Oid relid, bool even_if_detached)
Definition partition.c:53
#define foreach_ptr(type, var, lst)
Definition pg_list.h:501
#define foreach_oid(var, lst)
Definition pg_list.h:503

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

Referenced by ATExecMergePartitions(), and ATExecSplitPartition().

◆ ATAddCheckNNConstraint()

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

Definition at line 9997 of file tablecmds.c.

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

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

10155{
10168 bool with_period;
10170 int i;
10171 int numfks,
10172 numpks,
10174 Oid indexOid;
10175 bool old_check_ok;
10176 ObjectAddress address;
10178
10179 /*
10180 * Grab ShareRowExclusiveLock on the pk table, so that someone doesn't
10181 * delete rows out from under us.
10182 */
10183 if (OidIsValid(fkconstraint->old_pktable_oid))
10185 else
10187
10188 /*
10189 * Validity checks (permission checks wait till we have the column
10190 * numbers)
10191 */
10192 if (!recurse && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
10193 ereport(ERROR,
10195 errmsg("cannot use ONLY for foreign key on partitioned table \"%s\" referencing relation \"%s\"",
10198
10199 if (pkrel->rd_rel->relkind != RELKIND_RELATION &&
10200 pkrel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
10201 ereport(ERROR,
10203 errmsg("referenced relation \"%s\" is not a table",
10205
10207 ereport(ERROR,
10209 errmsg("permission denied: \"%s\" is a system catalog",
10211
10212 /*
10213 * References from permanent or unlogged tables to temp tables, and from
10214 * permanent tables to unlogged tables, are disallowed because the
10215 * referenced data can vanish out from under us. References from temp
10216 * tables to any other table type are also disallowed, because other
10217 * backends might need to run the RI triggers on the perm table, but they
10218 * can't reliably see tuples in the local buffers of other backends.
10219 */
10220 switch (rel->rd_rel->relpersistence)
10221 {
10224 ereport(ERROR,
10226 errmsg("constraints on permanent tables may reference only permanent tables")));
10227 break;
10230 && pkrel->rd_rel->relpersistence != RELPERSISTENCE_UNLOGGED)
10231 ereport(ERROR,
10233 errmsg("constraints on unlogged tables may reference only permanent or unlogged tables")));
10234 break;
10236 if (pkrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
10237 ereport(ERROR,
10239 errmsg("constraints on temporary tables may reference only temporary tables")));
10240 if (!pkrel->rd_islocaltemp || !rel->rd_islocaltemp)
10241 ereport(ERROR,
10243 errmsg("constraints on temporary tables must involve temporary tables of this session")));
10244 break;
10245 }
10246
10247 /*
10248 * Look up the referencing attributes to make sure they exist, and record
10249 * their attnums and type and collation OIDs.
10250 */
10252 fkconstraint->fk_attrs,
10254 with_period = fkconstraint->fk_with_period || fkconstraint->pk_with_period;
10255 if (with_period && !fkconstraint->fk_with_period)
10256 ereport(ERROR,
10258 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10259
10261 fkconstraint->fk_del_set_cols,
10266 fkconstraint->fk_del_set_cols);
10267
10268 /*
10269 * If the attribute list for the referenced table was omitted, lookup the
10270 * definition of the primary key and use it. Otherwise, validate the
10271 * supplied attribute list. In either case, discover the index OID and
10272 * index opclasses, and the attnums and type and collation OIDs of the
10273 * attributes.
10274 */
10275 if (fkconstraint->pk_attrs == NIL)
10276 {
10278 &fkconstraint->pk_attrs,
10281
10282 /* If the primary key uses WITHOUT OVERLAPS, the fk must use PERIOD */
10283 if (pk_has_without_overlaps && !fkconstraint->fk_with_period)
10284 ereport(ERROR,
10286 errmsg("foreign key uses PERIOD on the referenced table but not the referencing table"));
10287 }
10288 else
10289 {
10291 fkconstraint->pk_attrs,
10293
10294 /* Since we got pk_attrs, one should be a period. */
10295 if (with_period && !fkconstraint->pk_with_period)
10296 ereport(ERROR,
10298 errmsg("foreign key uses PERIOD on the referencing table but not the referenced table"));
10299
10300 /* Look for an index matching the column list */
10303 }
10304
10305 /*
10306 * If the referenced primary key has WITHOUT OVERLAPS, the foreign key
10307 * must use PERIOD.
10308 */
10310 ereport(ERROR,
10312 errmsg("foreign key must use PERIOD when referencing a primary key using WITHOUT OVERLAPS"));
10313
10314 /*
10315 * Now we can check permissions.
10316 */
10318
10319 /*
10320 * Check some things for generated columns.
10321 */
10322 for (i = 0; i < numfks; i++)
10323 {
10324 char attgenerated = TupleDescAttr(RelationGetDescr(rel), fkattnum[i] - 1)->attgenerated;
10325
10326 if (attgenerated)
10327 {
10328 /*
10329 * Check restrictions on UPDATE/DELETE actions, per SQL standard
10330 */
10331 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10332 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT ||
10333 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE)
10334 ereport(ERROR,
10336 errmsg("invalid %s action for foreign key constraint containing generated column",
10337 "ON UPDATE")));
10338 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10339 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10340 ereport(ERROR,
10342 errmsg("invalid %s action for foreign key constraint containing generated column",
10343 "ON DELETE")));
10344 }
10345
10346 /*
10347 * FKs on virtual columns are not supported. This would require
10348 * various additional support in ri_triggers.c, including special
10349 * handling in ri_NullCheck(), ri_KeysEqual(),
10350 * RI_FKey_fk_upd_check_required() (since all virtual columns appear
10351 * as NULL there). Also not really practical as long as you can't
10352 * index virtual columns.
10353 */
10354 if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
10355 ereport(ERROR,
10357 errmsg("foreign key constraints on virtual generated columns are not supported")));
10358 }
10359
10360 /*
10361 * Some actions are currently unsupported for foreign keys using PERIOD.
10362 */
10363 if (fkconstraint->fk_with_period)
10364 {
10365 if (fkconstraint->fk_upd_action == FKCONSTR_ACTION_RESTRICT ||
10366 fkconstraint->fk_upd_action == FKCONSTR_ACTION_CASCADE ||
10367 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETNULL ||
10368 fkconstraint->fk_upd_action == FKCONSTR_ACTION_SETDEFAULT)
10369 ereport(ERROR,
10371 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10372 "ON UPDATE"));
10373
10374 if (fkconstraint->fk_del_action == FKCONSTR_ACTION_RESTRICT ||
10375 fkconstraint->fk_del_action == FKCONSTR_ACTION_CASCADE ||
10376 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETNULL ||
10377 fkconstraint->fk_del_action == FKCONSTR_ACTION_SETDEFAULT)
10378 ereport(ERROR,
10380 errmsg("unsupported %s action for foreign key constraint using PERIOD",
10381 "ON DELETE"));
10382 }
10383
10384 /*
10385 * Look up the equality operators to use in the constraint.
10386 *
10387 * Note that we have to be careful about the difference between the actual
10388 * PK column type and the opclass' declared input type, which might be
10389 * only binary-compatible with it. The declared opcintype is the right
10390 * thing to probe pg_amop with.
10391 */
10392 if (numfks != numpks)
10393 ereport(ERROR,
10395 errmsg("number of referencing and referenced columns for foreign key disagree")));
10396
10397 /*
10398 * On the strength of a previous constraint, we might avoid scanning
10399 * tables to validate this one. See below.
10400 */
10401 old_check_ok = (fkconstraint->old_conpfeqop != NIL);
10402 Assert(!old_check_ok || numfks == list_length(fkconstraint->old_conpfeqop));
10403
10404 for (i = 0; i < numpks; i++)
10405 {
10406 Oid pktype = pktypoid[i];
10407 Oid fktype = fktypoid[i];
10408 Oid fktyped;
10409 Oid pkcoll = pkcolloid[i];
10410 Oid fkcoll = fkcolloid[i];
10413 Oid amid;
10414 Oid opfamily;
10415 Oid opcintype;
10416 bool for_overlaps;
10417 CompareType cmptype;
10418 Oid pfeqop;
10419 Oid ppeqop;
10420 Oid ffeqop;
10423
10424 /* We need several fields out of the pg_opclass entry */
10427 elog(ERROR, "cache lookup failed for opclass %u", opclasses[i]);
10429 amid = cla_tup->opcmethod;
10430 opfamily = cla_tup->opcfamily;
10431 opcintype = cla_tup->opcintype;
10433
10434 /*
10435 * Get strategy number from index AM.
10436 *
10437 * For a normal foreign-key constraint, this should not fail, since we
10438 * already checked that the index is unique and should therefore have
10439 * appropriate equal operators. For a period foreign key, this could
10440 * fail if we selected a non-matching exclusion constraint earlier.
10441 * (XXX Maybe we should do these lookups earlier so we don't end up
10442 * doing that.)
10443 */
10444 for_overlaps = with_period && i == numpks - 1;
10446 eqstrategy = IndexAmTranslateCompareType(cmptype, amid, opfamily, true);
10448 ereport(ERROR,
10451 ? errmsg("could not identify an overlaps operator for foreign key")
10452 : errmsg("could not identify an equality operator for foreign key"),
10453 errdetail("Could not translate compare type %d for operator family \"%s\" of access method \"%s\".",
10454 cmptype, get_opfamily_name(opfamily, false), get_am_name(amid)));
10455
10456 /*
10457 * There had better be a primary equality operator for the index.
10458 * We'll use it for PK = PK comparisons.
10459 */
10460 ppeqop = get_opfamily_member(opfamily, opcintype, opcintype,
10461 eqstrategy);
10462
10463 if (!OidIsValid(ppeqop))
10464 elog(ERROR, "missing operator %d(%u,%u) in opfamily %u",
10465 eqstrategy, opcintype, opcintype, opfamily);
10466
10467 /*
10468 * Are there equality operators that take exactly the FK type? Assume
10469 * we should look through any domain here.
10470 */
10472
10473 pfeqop = get_opfamily_member(opfamily, opcintype, fktyped,
10474 eqstrategy);
10475 if (OidIsValid(pfeqop))
10476 {
10479 eqstrategy);
10480 }
10481 else
10482 {
10483 /* keep compiler quiet */
10486 }
10487
10488 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10489 {
10490 /*
10491 * Otherwise, look for an implicit cast from the FK type to the
10492 * opcintype, and if found, use the primary equality operator.
10493 * This is a bit tricky because opcintype might be a polymorphic
10494 * type such as ANYARRAY or ANYENUM; so what we have to test is
10495 * whether the two actual column types can be concurrently cast to
10496 * that type. (Otherwise, we'd fail to reject combinations such
10497 * as int[] and point[].)
10498 */
10499 Oid input_typeids[2];
10501
10502 input_typeids[0] = pktype;
10503 input_typeids[1] = fktype;
10504 target_typeids[0] = opcintype;
10505 target_typeids[1] = opcintype;
10508 {
10509 pfeqop = ffeqop = ppeqop;
10510 pfeqop_right = opcintype;
10511 }
10512 }
10513
10514 if (!(OidIsValid(pfeqop) && OidIsValid(ffeqop)))
10515 ereport(ERROR,
10517 errmsg("foreign key constraint \"%s\" cannot be implemented",
10518 fkconstraint->conname),
10519 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10520 "are of incompatible types: %s and %s.",
10521 strVal(list_nth(fkconstraint->fk_attrs, i)),
10522 strVal(list_nth(fkconstraint->pk_attrs, i)),
10525
10526 /*
10527 * This shouldn't be possible, but better check to make sure we have a
10528 * consistent state for the check below.
10529 */
10531 elog(ERROR, "key columns are not both collatable");
10532
10534 {
10535 bool pkcolldet;
10536 bool fkcolldet;
10537
10540
10541 /*
10542 * SQL requires that both collations are the same. This is
10543 * because we need a consistent notion of equality on both
10544 * columns. We relax this by allowing different collations if
10545 * they are both deterministic. (This is also for backward
10546 * compatibility, because PostgreSQL has always allowed this.)
10547 */
10548 if ((!pkcolldet || !fkcolldet) && pkcoll != fkcoll)
10549 ereport(ERROR,
10551 errmsg("foreign key constraint \"%s\" cannot be implemented", fkconstraint->conname),
10552 errdetail("Key columns \"%s\" of the referencing table and \"%s\" of the referenced table "
10553 "have incompatible collations: \"%s\" and \"%s\". "
10554 "If either collation is nondeterministic, then both collations have to be the same.",
10555 strVal(list_nth(fkconstraint->fk_attrs, i)),
10556 strVal(list_nth(fkconstraint->pk_attrs, i)),
10559 }
10560
10561 if (old_check_ok)
10562 {
10563 /*
10564 * When a pfeqop changes, revalidate the constraint. We could
10565 * permit intra-opfamily changes, but that adds subtle complexity
10566 * without any concrete benefit for core types. We need not
10567 * assess ppeqop or ffeqop, which RI_Initial_Check() does not use.
10568 */
10570 old_pfeqop_item = lnext(fkconstraint->old_conpfeqop,
10572 }
10573 if (old_check_ok)
10574 {
10584 fkattnum[i] - 1);
10585
10586 /*
10587 * Identify coercion pathways from each of the old and new FK-side
10588 * column types to the right (foreign) operand type of the pfeqop.
10589 * We may assume that pg_constraint.conkey is not changing.
10590 */
10591 old_fktype = attr->atttypid;
10594 &old_castfunc);
10596 &new_castfunc);
10597
10598 old_fkcoll = attr->attcollation;
10600
10601 /*
10602 * Upon a change to the cast from the FK column to its pfeqop
10603 * operand, revalidate the constraint. For this evaluation, a
10604 * binary coercion cast is equivalent to no cast at all. While
10605 * type implementors should design implicit casts with an eye
10606 * toward consistency of operations like equality, we cannot
10607 * assume here that they have done so.
10608 *
10609 * A function with a polymorphic argument could change behavior
10610 * arbitrarily in response to get_fn_expr_argtype(). Therefore,
10611 * when the cast destination is polymorphic, we only avoid
10612 * revalidation if the input type has not changed at all. Given
10613 * just the core data types and operator classes, this requirement
10614 * prevents no would-be optimizations.
10615 *
10616 * If the cast converts from a base type to a domain thereon, then
10617 * that domain type must be the opcintype of the unique index.
10618 * Necessarily, the primary key column must then be of the domain
10619 * type. Since the constraint was previously valid, all values on
10620 * the foreign side necessarily exist on the primary side and in
10621 * turn conform to the domain. Consequently, we need not treat
10622 * domains specially here.
10623 *
10624 * If the collation changes, revalidation is required, unless both
10625 * collations are deterministic, because those share the same
10626 * notion of equality (because texteq reduces to bitwise
10627 * equality).
10628 *
10629 * We need not directly consider the PK type. It's necessarily
10630 * binary coercible to the opcintype of the unique index column,
10631 * and ri_triggers.c will only deal with PK datums in terms of
10632 * that opcintype. Changing the opcintype also changes pfeqop.
10633 */
10637 new_fktype == old_fktype) &&
10638 (new_fkcoll == old_fkcoll ||
10640 }
10641
10645 }
10646
10647 /*
10648 * For FKs with PERIOD we need additional operators to check whether the
10649 * referencing row's range is contained by the aggregated ranges of the
10650 * referenced row(s). For rangetypes and multirangetypes this is
10651 * fk.periodatt <@ range_agg(pk.periodatt). Those are the only types we
10652 * support for now. FKs will look these up at "runtime", but we should
10653 * make sure the lookup works here, even if we don't use the values.
10654 */
10655 if (with_period)
10656 {
10660
10663 }
10664
10665 /* First, create the constraint catalog entry itself. */
10667 fkconstraint->conname, fkconstraint, rel, pkrel,
10668 indexOid,
10669 InvalidOid, /* no parent constraint */
10670 numfks,
10671 pkattnum,
10672 fkattnum,
10678 false,
10679 with_period);
10680
10681 /* Next process the action triggers at the referenced side and recurse */
10683 indexOid,
10684 address.objectId,
10685 numfks,
10686 pkattnum,
10687 fkattnum,
10695 with_period);
10696
10697 /* Lastly create the check triggers at the referencing side and recurse */
10699 indexOid,
10700 address.objectId,
10701 numfks,
10702 pkattnum,
10703 fkattnum,
10710 lockmode,
10712 with_period);
10713
10714 /*
10715 * Done. Close pk table, but keep lock until we've committed.
10716 */
10718
10719 return address;
10720}
StrategyNumber IndexAmTranslateCompareType(CompareType cmptype, Oid amoid, Oid opfamily, bool missing_ok)
Definition amapi.c:161
char * get_am_name(Oid amOid)
Definition amcmds.c:192
bool IsSystemRelation(Relation relation)
Definition catalog.c:74
CompareType
Definition cmptype.h:32
@ COMPARE_OVERLAP
Definition cmptype.h:40
@ COMPARE_EQ
Definition cmptype.h:36
char * format_type_be(Oid type_oid)
bool allowSystemTableMods
Definition globals.c:132
#define false
char * get_collation_name(Oid colloid)
Definition lsyscache.c:1253
Oid get_opfamily_member(Oid opfamily, Oid lefttype, Oid righttype, int16 strategy)
Definition lsyscache.c:170
bool get_collation_isdeterministic(Oid colloid)
Definition lsyscache.c:1272
char * get_opfamily_name(Oid opfid, bool missing_ok)
Definition lsyscache.c:1545
Oid getBaseType(Oid typid)
Definition lsyscache.c:2829
bool can_coerce_type(int nargs, const Oid *input_typeids, const Oid *target_typeids, CoercionContext ccontext)
CoercionPathType
#define FKCONSTR_ACTION_RESTRICT
#define FKCONSTR_ACTION_SETDEFAULT
#define FKCONSTR_ACTION_CASCADE
#define FKCONSTR_ACTION_SETNULL
FormData_pg_attribute * Form_pg_attribute
void FindFKPeriodOpers(Oid opclass, Oid *containedbyoperoid, Oid *aggedcontainedbyoperoid, Oid *intersectoperoid)
static void * list_nth(const List *list, int n)
Definition pg_list.h:331
static ListCell * list_head(const List *l)
Definition pg_list.h:128
static ListCell * lnext(const List *l, const ListCell *c)
Definition pg_list.h:375
END_CATALOG_STRUCT typedef FormData_pg_opclass * Form_pg_opclass
Definition pg_opclass.h:87
@ COERCION_IMPLICIT
Definition primnodes.h: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, 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 12898 of file tablecmds.c.

12902{
12905 SysScanDesc scan;
12908
12909 /* Since this function recurses, it could be driven to stack overflow */
12911
12913 Assert(currcon->contype == CONSTRAINT_CHECK);
12914
12915 if (currcon->coninhcount <= 0)
12916 return false;
12917
12919
12925 true, NULL, 1, &skey);
12926
12928 {
12929 Oid parentoid;
12932 ScanKeyData pkey[3];
12933 HeapTuple parenttup;
12934
12936
12937 ScanKeyInit(&pkey[0],
12941 ScanKeyInit(&pkey[1],
12945 ScanKeyInit(&pkey[2],
12948 NameGetDatum(&currcon->conname));
12949
12951 true, NULL, 3, pkey);
12952
12953 /*
12954 * ConstraintRelidTypidNameIndexId is unique on (conrelid, contypid,
12955 * conname), so this loop body executes at most once per parent.
12956 */
12957 while (HeapTupleIsValid(parenttup = systable_getnext(pscan)))
12958 {
12960
12961 parentcon = (Form_pg_constraint) GETSTRUCT(parenttup);
12962
12963 if (parentcon->contype != CONSTRAINT_CHECK ||
12964 parentcon->connoinherit ||
12965 !parentcon->conenforced)
12966 continue;
12967
12968 if (!constraints_equivalent(parenttup, contuple,
12970 elog(ERROR, "child table \"%s\" has different definition for check constraint \"%s\"",
12972 NameStr(parentcon->conname));
12973
12974 /*
12975 * A parent listed in changing_conids is being changed by the same
12976 * ALTER, but it may not have been updated yet. For regular
12977 * inheritance, recurse upward to check whether an equivalent
12978 * enforced parent outside the ALTER will make it remain enforced.
12979 * Partitions cannot have multiple parents, so they do not need
12980 * this check.
12981 */
12982 if (!rel->rd_rel->relispartition &&
12984 {
12986
12987 if (parentrel == NULL)
12989
12991 parentrel,
12992 parenttup,
12995 continue;
12996 }
12997
12999 if (parentrel != NULL)
13002 systable_endscan(scan);
13004 return true;
13005 }
13006
13007 if (parentrel != NULL)
13010 }
13011
13012 systable_endscan(scan);
13014
13015 return false;
13016}
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 6958 of file tablecmds.c.

6959{
6960 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
6961 {
6962 List *inh;
6963 ListCell *cell;
6964
6965 inh = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
6966 /* first element is the parent rel; must ignore it */
6967 for_each_from(cell, inh, 1)
6968 {
6970
6971 /* find_all_inheritors already got lock */
6975 }
6976 list_free(inh);
6977 }
6978}
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 15153 of file tablecmds.c.

15154{
15155 Assert(expr != NULL);
15156
15157 for (;;)
15158 {
15159 /* only one varno, so no need to check that */
15160 if (IsA(expr, Var) && ((Var *) expr)->varattno == varattno)
15161 return false;
15162 else if (IsA(expr, RelabelType))
15163 expr = (Node *) ((RelabelType *) expr)->arg;
15164 else if (IsA(expr, CoerceToDomain))
15165 {
15166 CoerceToDomain *d = (CoerceToDomain *) expr;
15167
15169 return true;
15170 expr = (Node *) d->arg;
15171 }
15172 else if (IsA(expr, FuncExpr))
15173 {
15174 FuncExpr *f = (FuncExpr *) expr;
15175
15176 switch (f->funcid)
15177 {
15181 return true;
15182 else
15183 expr = linitial(f->args);
15184 break;
15185 default:
15186 return true;
15187 }
15188 }
15189 else
15190 return true;
15191 }
15192}
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 4939 of file tablecmds.c.

4942{
4943 List *wqueue = NIL;
4944 ListCell *lcmd;
4945
4946 /* Phase 1: preliminary examination of commands, create work queue */
4947 foreach(lcmd, cmds)
4948 {
4950
4951 ATPrepCmd(&wqueue, rel, cmd, recurse, false, lockmode, context);
4952 }
4953
4954 /* Close the relation, but keep lock until commit */
4955 relation_close(rel, NoLock);
4956
4957 /* Phase 2: update system catalogs */
4958 ATRewriteCatalogs(&wqueue, lockmode, context);
4959
4960 /* Phase 3: scan/rewrite tables as needed, and run afterStmts */
4961 ATRewriteTables(parsetree, &wqueue, lockmode, context);
4962}
static void ATRewriteCatalogs(List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:5377
static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:4974
static void ATRewriteTables(AlterTableStmt *parsetree, List **wqueue, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:5929

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

22481{
22482 List *constraints;
22483 ListCell *cell;
22484
22485 constraints = GetParentedForeignKeyRefs(partition);
22486
22487 foreach(cell, constraints)
22488 {
22489 Oid constrOid = lfirst_oid(cell);
22490 HeapTuple tuple;
22492 Relation rel;
22493 Trigger trig = {0};
22494
22496 if (!HeapTupleIsValid(tuple))
22497 elog(ERROR, "cache lookup failed for constraint %u", constrOid);
22499
22500 Assert(OidIsValid(constrForm->conparentid));
22502
22503 /* prevent data changes into the referencing table until commit */
22504 rel = table_open(constrForm->conrelid, ShareLock);
22505
22506 trig.tgoid = InvalidOid;
22507 trig.tgname = NameStr(constrForm->conname);
22508 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
22509 trig.tgisinternal = true;
22510 trig.tgconstrrelid = RelationGetRelid(partition);
22511 trig.tgconstrindid = constrForm->conindid;
22512 trig.tgconstraint = constrForm->oid;
22513 trig.tgdeferrable = false;
22514 trig.tginitdeferred = false;
22515 /* we needn't fill in remaining fields */
22516
22518
22519 ReleaseSysCache(tuple);
22520
22521 table_close(rel, NoLock);
22522 }
22523}
#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 19925 of file tablecmds.c.

19927{
19929
19930 foreach(cur_item, on_commits)
19931 {
19933
19934 if (!isCommit && oc->creating_subid == mySubid)
19935 {
19936 /* cur_item must be removed */
19938 pfree(oc);
19939 }
19940 else
19941 {
19942 /* cur_item must be preserved */
19943 if (oc->creating_subid == mySubid)
19944 oc->creating_subid = parentSubid;
19945 if (oc->deleting_subid == mySubid)
19946 oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId;
19947 }
19948 }
19949}
#define InvalidSubTransactionId
Definition c.h:798
#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 19893 of file tablecmds.c.

19894{
19896
19897 foreach(cur_item, on_commits)
19898 {
19900
19901 if (isCommit ? oc->deleting_subid != InvalidSubTransactionId :
19902 oc->creating_subid != InvalidSubTransactionId)
19903 {
19904 /* cur_item must be removed */
19906 pfree(oc);
19907 }
19908 else
19909 {
19910 /* cur_item must be preserved */
19911 oc->creating_subid = InvalidSubTransactionId;
19912 oc->deleting_subid = InvalidSubTransactionId;
19913 }
19914 }
19915}

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

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

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

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

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

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

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

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

Referenced by ATExecCmd().

◆ ATExecAddIndexConstraint()

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

Definition at line 9789 of file tablecmds.c.

9791{
9792 Oid index_oid = stmt->indexOid;
9793 Relation indexRel;
9794 char *indexName;
9795 IndexInfo *indexInfo;
9796 char *constraintName;
9797 char constraintType;
9798 ObjectAddress address;
9799 uint16 flags;
9800
9803 Assert(stmt->isconstraint);
9804
9805 /*
9806 * Doing this on partitioned tables is not a simple feature to implement,
9807 * so let's punt for now.
9808 */
9809 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
9810 ereport(ERROR,
9812 errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX is not supported on partitioned tables")));
9813
9815
9817
9818 indexInfo = BuildIndexInfo(indexRel);
9819
9820 /* this should have been checked at parse time */
9821 if (!indexInfo->ii_Unique)
9822 elog(ERROR, "index \"%s\" is not unique", indexName);
9823
9824 /*
9825 * Determine name to assign to constraint. We require a constraint to
9826 * have the same name as the underlying index; therefore, use the index's
9827 * existing name as the default constraint name, and if the user
9828 * explicitly gives some other name for the constraint, rename the index
9829 * to match.
9830 */
9831 constraintName = stmt->idxname;
9832 if (constraintName == NULL)
9834 else if (strcmp(constraintName, indexName) != 0)
9835 {
9837 (errmsg("ALTER TABLE / ADD CONSTRAINT USING INDEX will rename index \"%s\" to \"%s\"",
9840 }
9841
9842 /* Extra checks needed if making primary key */
9843 if (stmt->primary)
9844 index_check_primary_key(rel, indexInfo, true, stmt);
9845
9846 /* Note we currently don't support EXCLUSION constraints here */
9847 if (stmt->primary)
9849 else
9851
9852 /* Create the catalog entries for the constraint */
9855 (stmt->initdeferred ? INDEX_CONSTR_CREATE_INIT_DEFERRED : 0) |
9856 (stmt->deferrable ? INDEX_CONSTR_CREATE_DEFERRABLE : 0) |
9858
9859 address = index_constraint_create(rel,
9860 index_oid,
9861 InvalidOid,
9862 indexInfo,
9865 flags,
9867 false); /* is_internal */
9868
9869 index_close(indexRel, NoLock);
9870
9871 return address;
9872}
uint16_t uint16
Definition c.h:679
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:4334

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

17732{
17734 List *children;
17735 ObjectAddress address;
17736 const char *trigger_name;
17737
17738 /*
17739 * A self-exclusive lock is needed here. See the similar case in
17740 * MergeAttributes() for a full explanation.
17741 */
17743
17744 /*
17745 * Must be owner of both parent and child -- child was checked by
17746 * ATSimplePermissions call in ATPrepCmd
17747 */
17750
17751 /* Permanent rels cannot inherit from temporary ones */
17752 if (parent_rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
17753 child_rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
17754 ereport(ERROR,
17756 errmsg("cannot inherit from temporary relation \"%s\"",
17758
17759 /* If parent rel is temp, it must belong to this session */
17761 ereport(ERROR,
17763 errmsg("cannot inherit from temporary relation of another session")));
17764
17765 /* Ditto for the child */
17767 ereport(ERROR,
17769 errmsg("cannot inherit to temporary relation of another session")));
17770
17771 /* Prevent partitioned tables from becoming inheritance parents */
17772 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
17773 ereport(ERROR,
17775 errmsg("cannot inherit from partitioned table \"%s\"",
17776 parent->relname)));
17777
17778 /* Likewise for partitions */
17779 if (parent_rel->rd_rel->relispartition)
17780 ereport(ERROR,
17782 errmsg("cannot inherit from a partition")));
17783
17784 /*
17785 * Prevent circularity by seeing if proposed parent inherits from child.
17786 * (In particular, this disallows making a rel inherit from itself.)
17787 *
17788 * This is not completely bulletproof because of race conditions: in
17789 * multi-level inheritance trees, someone else could concurrently be
17790 * making another inheritance link that closes the loop but does not join
17791 * either of the rels we have locked. Preventing that seems to require
17792 * exclusive locks on the entire inheritance tree, which is a cure worse
17793 * than the disease. find_all_inheritors() will cope with circularity
17794 * anyway, so don't sweat it too much.
17795 *
17796 * We use weakest lock we can on child's children, namely AccessShareLock.
17797 */
17800
17802 ereport(ERROR,
17804 errmsg("circular inheritance not allowed"),
17805 errdetail("\"%s\" is already a child of \"%s\".",
17806 parent->relname,
17808
17809 /*
17810 * If child_rel has row-level triggers with transition tables, we
17811 * currently don't allow it to become an inheritance child. See also
17812 * prohibitions in ATExecAttachPartition() and CreateTrigger().
17813 */
17815 if (trigger_name != NULL)
17816 ereport(ERROR,
17818 errmsg("trigger \"%s\" prevents table \"%s\" from becoming an inheritance child",
17820 errdetail("ROW triggers with transition tables are not supported in inheritance hierarchies.")));
17821
17822 /* OK to create inheritance */
17824
17827
17828 /* keep our lock on the parent relation until commit */
17830
17831 return address;
17832}
#define RELATION_IS_OTHER_TEMP(relation)
Definition rel.h:678
char * relname
Definition primnodes.h:84
static void CreateInheritance(Relation child_rel, Relation parent_rel, bool ispartition)
const char * FindTriggerIncompatibleWithInheritance(TriggerDesc *trigdesc)
Definition trigger.c:2279

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

Referenced by ATExecCmd().

◆ ATExecAddOf()

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

Definition at line 18682 of file tablecmds.c.

18683{
18684 Oid relid = RelationGetRelid(rel);
18687 Oid typeid;
18690 SysScanDesc scan;
18693 type_attno;
18697 typeobj;
18699
18700 /* Validate the type. */
18701 typetuple = typenameType(NULL, ofTypename, NULL);
18704 typeid = typeform->oid;
18705
18706 /* Fail if the table has any inheritance parents. */
18708 ScanKeyInit(&key,
18711 ObjectIdGetDatum(relid));
18713 true, NULL, 1, &key);
18715 ereport(ERROR,
18717 errmsg("typed tables cannot inherit")));
18718 systable_endscan(scan);
18720
18721 /*
18722 * Check the tuple descriptors for compatibility. Unlike inheritance, we
18723 * require that the order also match. However, attnotnull need not match.
18724 */
18727 table_attno = 1;
18728 for (type_attno = 1; type_attno <= typeTupleDesc->natts; type_attno++)
18729 {
18731 table_attr;
18732 const char *type_attname,
18734
18735 /* Get the next non-dropped type attribute. */
18737 if (type_attr->attisdropped)
18738 continue;
18739 type_attname = NameStr(type_attr->attname);
18740
18741 /* Get the next non-dropped table attribute. */
18742 do
18743 {
18744 if (table_attno > tableTupleDesc->natts)
18745 ereport(ERROR,
18747 errmsg("table is missing column \"%s\"",
18748 type_attname)));
18750 table_attno++;
18751 } while (table_attr->attisdropped);
18752 table_attname = NameStr(table_attr->attname);
18753
18754 /* Compare name. */
18756 ereport(ERROR,
18758 errmsg("table has column \"%s\" where type requires \"%s\"",
18760
18761 /* Compare type. */
18762 if (table_attr->atttypid != type_attr->atttypid ||
18763 table_attr->atttypmod != type_attr->atttypmod ||
18764 table_attr->attcollation != type_attr->attcollation)
18765 ereport(ERROR,
18767 errmsg("table \"%s\" has different type for column \"%s\"",
18769 }
18771
18772 /* Any remaining columns at the end of the table had better be dropped. */
18773 for (; table_attno <= tableTupleDesc->natts; table_attno++)
18774 {
18776 table_attno - 1);
18777
18778 if (!table_attr->attisdropped)
18779 ereport(ERROR,
18781 errmsg("table has extra column \"%s\"",
18782 NameStr(table_attr->attname))));
18783 }
18784
18785 /* If the table was already typed, drop the existing dependency. */
18786 if (rel->rd_rel->reloftype)
18787 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18789
18790 /* Record a dependency on the new type. */
18791 tableobj.classId = RelationRelationId;
18792 tableobj.objectId = relid;
18793 tableobj.objectSubId = 0;
18794 typeobj.classId = TypeRelationId;
18795 typeobj.objectId = typeid;
18796 typeobj.objectSubId = 0;
18798
18799 /* Update pg_class.reloftype */
18803 elog(ERROR, "cache lookup failed for relation %u", relid);
18804 ((Form_pg_class) GETSTRUCT(classtuple))->reloftype = typeid;
18806
18808
18811
18813
18814 return typeobj;
18815}
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:7240
#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 9768 of file tablecmds.c.

9770{
9771 ObjectAddress address;
9772
9774
9775 /* The CreateStatsStmt has already been through transformStatsStmt */
9776 Assert(stmt->transformed);
9777
9778 address = CreateStatistics(stmt, !is_rebuild);
9779
9780 return address;
9781}
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 12654 of file tablecmds.c.

12659{
12661 Relation rel;
12662 bool changed = false;
12663 List *children = NIL;
12664 bool target_enforced = cmdcon->is_enforced;
12666
12667 /* Since this function recurses, it could be driven to stack overflow */
12669
12670 Assert(cmdcon->alterEnforceability);
12671
12673
12674 Assert(currcon->contype == CONSTRAINT_CHECK);
12675
12676 /*
12677 * Parent relation already locked by caller, children will be locked by
12678 * find_all_inheritors. So NoLock is fine here.
12679 */
12680 rel = table_open(currcon->conrelid, NoLock);
12681
12682 /*
12683 * When setting a constraint to NOT ENFORCED, check whether any matching
12684 * parent constraint remains ENFORCED and is not part of this ALTER.
12685 *
12686 * For a direct ALTER of an inherited constraint, reject the command,
12687 * because the child cannot be weakened while its parent remains enforced.
12688 *
12689 * During recursion, another parent outside this ALTER may still enforce
12690 * the same constraint in a regular inheritance hierarchy. In that case,
12691 * keep the child constraint ENFORCED so that its merged enforceability
12692 * still reflects the remaining enforced parent. Partitions do not need
12693 * this recursive parent check because a partition can have only one
12694 * direct parent.
12695 */
12696 if (!cmdcon->is_enforced &&
12697 (!recursing || !rel->rd_rel->relispartition) &&
12701 {
12702 if (!recursing)
12703 ereport(ERROR,
12705 errmsg("cannot mark inherited constraint \"%s\" as %s",
12706 NameStr(currcon->conname),
12707 "NOT ENFORCED"),
12708 errdetail("The matching constraint on parent table \"%s\" is %s.",
12709 get_rel_name(enforced_parentoid), "ENFORCED"));
12710
12711 target_enforced = true;
12712 }
12713
12714 /*
12715 * Update to the merged enforceability if needed. This may differ from the
12716 * requested enforceability when another matching parent constraint
12717 * remains enforced.
12718 */
12719 if (currcon->conenforced != target_enforced)
12720 {
12722
12725 changed = true;
12726 }
12727
12728 /*
12729 * Note that we must recurse even when trying to change a check constraint
12730 * to not enforced if it is already not enforced, in case descendant
12731 * constraints might be enforced and need to be changed to not enforced,
12732 * unless they still inherit an enforced constraint from another parent.
12733 * Conversely, we should do nothing if a constraint is being set to
12734 * enforced and is already enforced, as descendant constraints cannot be
12735 * different in that case.
12736 */
12737 if (!cmdcon->is_enforced || changed)
12738 {
12739 /*
12740 * If we're recursing, the parent has already done this, so skip it.
12741 * Also, if the constraint is a NO INHERIT constraint, we shouldn't
12742 * try to look for it in the children.
12743 */
12744 if (!recursing && !currcon->connoinherit)
12745 {
12747
12748 children = find_all_inheritors(RelationGetRelid(rel),
12749 lockmode, NULL);
12750
12751 /*
12752 * When setting NOT ENFORCED, build the set of equivalent CHECK
12753 * constraints that this command will attempt to change before
12754 * visiting descendants. The root itself has already been checked
12755 * above.
12756 */
12757 if (!cmdcon->is_enforced)
12759
12760 foreach_oid(childoid, children)
12761 {
12762 if (childoid == RelationGetRelid(rel))
12763 continue;
12764
12765 /*
12766 * If we are told not to recurse, there had better not be any
12767 * child tables, because we can't change constraint
12768 * enforceability on the parent unless we have changed
12769 * enforceability for all child.
12770 */
12771 if (!recurse)
12772 ereport(ERROR,
12774 errmsg("constraint must be altered on child tables too"),
12775 errhint("Do not specify the ONLY keyword."));
12776
12777 /*
12778 * It is sufficient to look up the constraint by name here.
12779 * Supported DDL ensures that inheritable CHECK constraints
12780 * with the same name have equivalent definitions when they
12781 * are propagated to children or when inheritance is
12782 * established. All descendants returned by
12783 * find_all_inheritors must have this constraint: inherited
12784 * CHECK constraints propagate to all children at
12785 * inheritance-link creation time and cannot be dropped
12786 * independently on child tables.
12787 */
12788 if (!cmdcon->is_enforced)
12792 cmdcon->conname,
12793 false));
12794 }
12795 }
12796
12797 foreach_oid(childoid, children)
12798 {
12799 if (childoid == RelationGetRelid(rel))
12800 continue;
12801
12803 childoid, false, true,
12805 lockmode);
12806 }
12807 }
12808
12809 /*
12810 * Tell Phase 3 to check that the constraint is satisfied by existing
12811 * rows. We only need do this when altering the constraint from NOT
12812 * ENFORCED to ENFORCED.
12813 */
12814 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12815 !currcon->conenforced &&
12817 {
12818 AlteredTableInfo *tab;
12820 Datum val;
12821 char *conbin;
12822
12824 newcon->name = pstrdup(NameStr(currcon->conname));
12825 newcon->contype = CONSTR_CHECK;
12826
12831
12832 /* Find or create work queue entry for this table */
12833 tab = ATGetQueueEntry(wqueue, rel);
12834 tab->constraints = lappend(tab->constraints, newcon);
12835 }
12836
12837 table_close(rel, NoLock);
12838
12839 return changed;
12840}
#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 16429 of file tablecmds.c.

16433{
16436 ForeignServer *server;
16438 HeapTuple tuple;
16439 HeapTuple newtuple;
16440 bool isnull;
16444 Datum datum;
16448 ObjectAddress address;
16449
16450 if (options == NIL)
16451 return InvalidObjectAddress;
16452
16453 /* First, determine FDW validator associated to the foreign table. */
16456 if (!HeapTupleIsValid(tuple))
16457 ereport(ERROR,
16459 errmsg("foreign table \"%s\" does not exist",
16462 server = GetForeignServer(fttableform->ftserver);
16463 fdw = GetForeignDataWrapper(server->fdwid);
16464
16466 ReleaseSysCache(tuple);
16467
16470 if (!HeapTupleIsValid(tuple))
16471 ereport(ERROR,
16473 errmsg("column \"%s\" of relation \"%s\" does not exist",
16475
16476 /* Prevent them from altering a system attribute */
16478 attnum = atttableform->attnum;
16479 if (attnum <= 0)
16480 ereport(ERROR,
16482 errmsg("cannot alter system column \"%s\"", colName)));
16483
16484
16485 /* Initialize buffers for new tuple values */
16486 memset(repl_val, 0, sizeof(repl_val));
16487 memset(repl_null, false, sizeof(repl_null));
16488 memset(repl_repl, false, sizeof(repl_repl));
16489
16490 /* Extract the current options */
16491 datum = SysCacheGetAttr(ATTNAME,
16492 tuple,
16494 &isnull);
16495 if (isnull)
16496 datum = PointerGetDatum(NULL);
16497
16498 /* Transform the options */
16500 datum,
16501 options,
16502 fdw->fdwvalidator);
16503
16504 if (DatumGetPointer(datum) != NULL)
16506 else
16508
16510
16511 /* Everything looks good - update the tuple */
16512
16513 newtuple = heap_modify_tuple(tuple, RelationGetDescr(attrel),
16515
16516 CatalogTupleUpdate(attrel, &newtuple->t_self, newtuple);
16517
16519 RelationGetRelid(rel),
16520 atttableform->attnum);
16522 RelationGetRelid(rel), attnum);
16523
16524 ReleaseSysCache(tuple);
16525
16527
16528 heap_freetuple(newtuple);
16529
16530 return address;
16531}
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 15200 of file tablecmds.c.

15202{
15203 char *colName = cmd->name;
15204 ColumnDef *def = (ColumnDef *) cmd->def;
15205 TypeName *typeName = def->typeName;
15208 attOldTup;
15212 Oid targettype;
15213 int32 targettypmod;
15218 ScanKeyData key[3];
15219 SysScanDesc scan;
15221 ObjectAddress address;
15222
15223 /*
15224 * Clear all the missing values if we're rewriting the table, since this
15225 * renders them pointless.
15226 */
15227 if (tab->rewrite)
15228 {
15230
15234 /* make sure we don't conflict with later attribute modifications */
15236 }
15237
15239
15240 /* Look up the target column */
15242 if (!HeapTupleIsValid(heapTup)) /* shouldn't happen */
15243 ereport(ERROR,
15245 errmsg("column \"%s\" of relation \"%s\" does not exist",
15248 attnum = attTup->attnum;
15250
15251 /* Check for multiple ALTER TYPE on same column --- can't cope */
15252 if (attTup->atttypid != attOldTup->atttypid ||
15253 attTup->atttypmod != attOldTup->atttypmod)
15254 ereport(ERROR,
15256 errmsg("cannot alter type of column \"%s\" twice",
15257 colName)));
15258
15259 /* Look up the target type (should not fail, since prep found it) */
15260 typeTuple = typenameType(NULL, typeName, &targettypmod);
15262 targettype = tform->oid;
15263 /* And the collation */
15264 targetcollid = GetColumnDefCollation(NULL, def, targettype);
15265
15266 /*
15267 * If there is a default expression for the column, get it and ensure we
15268 * can coerce it to the new datatype. (We must do this before changing
15269 * the column type, because build_column_default itself will try to
15270 * coerce, and will not issue the error message we want if it fails.)
15271 *
15272 * We remove any implicit coercion steps at the top level of the old
15273 * default expression; this has been agreed to satisfy the principle of
15274 * least surprise. (The conversion to the new column type should act like
15275 * it started from what the user sees as the stored expression, and the
15276 * implicit coercions aren't going to be shown.)
15277 */
15278 if (attTup->atthasdef)
15279 {
15283 defaultexpr = coerce_to_target_type(NULL, /* no UNKNOWN params */
15285 targettype, targettypmod,
15288 -1);
15289 if (defaultexpr == NULL)
15290 {
15291 if (attTup->attgenerated)
15292 ereport(ERROR,
15294 errmsg("generation expression for column \"%s\" cannot be cast automatically to type %s",
15295 colName, format_type_be(targettype))));
15296 else
15297 ereport(ERROR,
15299 errmsg("default for column \"%s\" cannot be cast automatically to type %s",
15300 colName, format_type_be(targettype))));
15301 }
15302 }
15303 else
15304 defaultexpr = NULL;
15305
15306 /*
15307 * Find everything that depends on the column (constraints, indexes, etc),
15308 * and record enough information to let us recreate the objects.
15309 *
15310 * The actual recreation does not happen here, but only after we have
15311 * performed all the individual ALTER TYPE operations. We have to save
15312 * the info before executing ALTER TYPE, though, else the deparser will
15313 * get confused.
15314 */
15316
15317 /*
15318 * Now scan for dependencies of this column on other things. The only
15319 * things we should find are the dependency on the column datatype and
15320 * possibly a collation dependency. Those can be removed.
15321 */
15323
15324 ScanKeyInit(&key[0],
15328 ScanKeyInit(&key[1],
15332 ScanKeyInit(&key[2],
15336
15338 NULL, 3, key);
15339
15341 {
15344
15345 foundObject.classId = foundDep->refclassid;
15346 foundObject.objectId = foundDep->refobjid;
15347 foundObject.objectSubId = foundDep->refobjsubid;
15348
15349 if (foundDep->deptype != DEPENDENCY_NORMAL)
15350 elog(ERROR, "found unexpected dependency type '%c'",
15351 foundDep->deptype);
15352 if (!(foundDep->refclassid == TypeRelationId &&
15353 foundDep->refobjid == attTup->atttypid) &&
15354 !(foundDep->refclassid == CollationRelationId &&
15355 foundDep->refobjid == attTup->attcollation))
15356 elog(ERROR, "found unexpected dependency for column: %s",
15358
15359 CatalogTupleDelete(depRel, &depTup->t_self);
15360 }
15361
15362 systable_endscan(scan);
15363
15365
15366 /*
15367 * Here we go --- change the recorded column type and collation. (Note
15368 * heapTup is a copy of the syscache entry, so okay to scribble on.) First
15369 * fix up the missing value if any.
15370 */
15371 if (attTup->atthasmissing)
15372 {
15374 bool missingNull;
15375
15376 /* if rewrite is true the missing value should already be cleared */
15377 Assert(tab->rewrite == 0);
15378
15379 /* Get the missing value datum */
15382 attrelation->rd_att,
15383 &missingNull);
15384
15385 /* if it's a null array there is nothing to do */
15386
15387 if (!missingNull)
15388 {
15389 /*
15390 * Get the datum out of the array and repack it in a new array
15391 * built with the new type data. We assume that since the table
15392 * doesn't need rewriting, the actual Datum doesn't need to be
15393 * changed, only the array metadata.
15394 */
15395
15396 int one = 1;
15397 bool isNull;
15399 bool nullsAtt[Natts_pg_attribute] = {0};
15400 bool replacesAtt[Natts_pg_attribute] = {0};
15402
15404 1,
15405 &one,
15406 0,
15407 attTup->attlen,
15408 attTup->attbyval,
15409 attTup->attalign,
15410 &isNull);
15412 1,
15413 targettype,
15414 tform->typlen,
15415 tform->typbyval,
15416 tform->typalign));
15417
15421
15425 heapTup = newTup;
15427 }
15428 }
15429
15430 attTup->atttypid = targettype;
15431 attTup->atttypmod = targettypmod;
15432 attTup->attcollation = targetcollid;
15433 if (list_length(typeName->arrayBounds) > PG_INT16_MAX)
15434 ereport(ERROR,
15436 errmsg("too many array dimensions"));
15437 attTup->attndims = list_length(typeName->arrayBounds);
15438 attTup->attlen = tform->typlen;
15439 attTup->attbyval = tform->typbyval;
15440 attTup->attalign = tform->typalign;
15441 attTup->attstorage = tform->typstorage;
15442 attTup->attcompression = InvalidCompressionMethod;
15443
15445
15447
15449
15450 /* Install dependencies on new datatype and collation */
15453
15454 /*
15455 * Drop any pg_statistic entry for the column, since it's now wrong type
15456 */
15458
15460 RelationGetRelid(rel), attnum);
15461
15462 /*
15463 * Update the default, if present, by brute force --- remove and re-add
15464 * the default. Probably unsafe to take shortcuts, since the new version
15465 * may well have additional dependencies. (It's okay to do this now,
15466 * rather than after other ALTER TYPE commands, since the default won't
15467 * depend on other column types.)
15468 */
15469 if (defaultexpr)
15470 {
15471 /*
15472 * If it's a GENERATED default, drop its dependency records, in
15473 * particular its INTERNAL dependency on the column, which would
15474 * otherwise cause dependency.c to refuse to perform the deletion.
15475 */
15476 if (attTup->attgenerated)
15477 {
15479
15480 if (!OidIsValid(attrdefoid))
15481 elog(ERROR, "could not find attrdef tuple for relation %u attnum %d",
15482 RelationGetRelid(rel), attnum);
15484 }
15485
15486 /*
15487 * Make updates-so-far visible, particularly the new pg_attribute row
15488 * which will be updated again.
15489 */
15491
15492 /*
15493 * We use RESTRICT here for safety, but at present we do not expect
15494 * anything to depend on the default.
15495 */
15497 true);
15498
15499 (void) StoreAttrDefault(rel, attnum, defaultexpr, true);
15500 }
15501
15503 RelationGetRelid(rel), attnum);
15504
15505 /* Cleanup */
15507
15508 return address;
15509}
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:726
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:296
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 12283 of file tablecmds.c.

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

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

13033{
13035 Oid refrelid;
13036 bool changed = false;
13037
13038 /* since this function recurses, it could be driven to stack overflow */
13040
13041 Assert(cmdcon->alterDeferrability);
13042
13044 refrelid = currcon->confrelid;
13045
13046 /* Should be foreign key constraint */
13047 Assert(currcon->contype == CONSTRAINT_FOREIGN);
13048
13049 /*
13050 * If called to modify a constraint that's already in the desired state,
13051 * silently do nothing.
13052 */
13053 if (currcon->condeferrable != cmdcon->deferrable ||
13054 currcon->condeferred != cmdcon->initdeferred)
13055 {
13057 changed = true;
13058
13059 /*
13060 * Now we need to update the multiple entries in pg_trigger that
13061 * implement the constraint.
13062 */
13064 cmdcon->deferrable,
13065 cmdcon->initdeferred, otherrelids);
13066 }
13067
13068 /*
13069 * If the table at either end of the constraint is partitioned, we need to
13070 * handle every constraint that is a child of this one.
13071 */
13072 if (recurse && changed &&
13073 (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13076 contuple, recurse, otherrelids,
13077 lockmode);
13078
13079 return changed;
13080}
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 13086 of file tablecmds.c.

13089{
13092 char *colName;
13093 List *children;
13094
13095 Assert(cmdcon->alterInheritability);
13096
13098
13099 /* The current implementation only works for NOT NULL constraints */
13100 Assert(currcon->contype == CONSTRAINT_NOTNULL);
13101
13102 /*
13103 * If called to modify a constraint that's already in the desired state,
13104 * silently do nothing.
13105 */
13106 if (cmdcon->noinherit == currcon->connoinherit)
13107 return false;
13108
13111
13112 /* Fetch the column number and name */
13114 colName = get_attname(currcon->conrelid, colNum, false);
13115
13116 /*
13117 * Propagate the change to children. For this subcommand type we don't
13118 * recursively affect children, just the immediate level.
13119 */
13121 lockmode);
13122 foreach_oid(childoid, children)
13123 {
13124 ObjectAddress addr;
13125
13126 if (cmdcon->noinherit)
13127 {
13130
13132 if (!childtup)
13133 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
13134 colName, childoid);
13136 Assert(childcon->coninhcount > 0);
13137 childcon->coninhcount--;
13138 childcon->conislocal = true;
13141 }
13142 else
13143 {
13145
13146 addr = ATExecSetNotNull(wqueue, childrel, NameStr(currcon->conname),
13147 colName, true, true, lockmode);
13148 if (OidIsValid(addr.objectId))
13151 }
13152 }
13153
13154 return true;
13155}
char * get_attname(Oid relid, AttrNumber attnum, bool missing_ok)
Definition lsyscache.c:1045
HeapTuple findNotNullConstraint(Oid relid, const char *colname)
AttrNumber extractNotNullColumn(HeapTuple constrTup)
static ObjectAddress ATExecSetNotNull(List **wqueue, Relation rel, char *conName, char *colName, bool recurse, bool recursing, LOCKMODE lockmode)
Definition tablecmds.c:8010

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

12520{
12522 Oid conoid;
12523 Relation rel;
12524 bool changed = false;
12525
12526 /* Since this function recurses, it could be driven to stack overflow */
12528
12529 Assert(cmdcon->alterEnforceability);
12530
12532 conoid = currcon->oid;
12533
12534 /* Should be foreign key constraint */
12535 Assert(currcon->contype == CONSTRAINT_FOREIGN);
12536
12537 rel = table_open(currcon->conrelid, lockmode);
12538
12539 if (currcon->conenforced != cmdcon->is_enforced)
12540 {
12542 changed = true;
12543 }
12544
12545 /* Drop triggers */
12546 if (!cmdcon->is_enforced)
12547 {
12548 /*
12549 * When setting a constraint to NOT ENFORCED, the constraint triggers
12550 * need to be dropped. Therefore, we must process the child relations
12551 * first, followed by the parent, to account for dependencies.
12552 */
12553 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12557 lockmode, InvalidOid, InvalidOid,
12559
12560 /* Drop all the triggers */
12562 }
12563 else if (changed) /* Create triggers */
12564 {
12569
12570 /* Prepare the minimal information required for trigger creation. */
12572
12573 fkconstraint->conname = pstrdup(NameStr(currcon->conname));
12574 fkconstraint->fk_matchtype = currcon->confmatchtype;
12575 fkconstraint->fk_upd_action = currcon->confupdtype;
12576 fkconstraint->fk_del_action = currcon->confdeltype;
12577 fkconstraint->deferrable = currcon->condeferrable;
12578 fkconstraint->initdeferred = currcon->condeferred;
12579
12580 /* Create referenced triggers */
12581 if (currcon->conrelid == fkrelid)
12583 currcon->confrelid,
12585 conoid,
12586 currcon->conindid,
12591
12592 /* Create referencing triggers */
12593 if (currcon->confrelid == pkrelid)
12595 pkrelid,
12597 conoid,
12598 currcon->conindid,
12603
12604 /*
12605 * Tell Phase 3 to check that the constraint is satisfied by existing
12606 * rows. Only applies to leaf partitions, and (for constraints that
12607 * reference a partitioned table) only if this is not one of the
12608 * pg_constraint rows that exist solely to support action triggers.
12609 */
12610 if (rel->rd_rel->relkind == RELKIND_RELATION &&
12611 currcon->confrelid == pkrelid)
12612 {
12613 AlteredTableInfo *tab;
12615
12617 newcon->name = fkconstraint->conname;
12618 newcon->contype = CONSTR_FOREIGN;
12619 newcon->refrelid = currcon->confrelid;
12620 newcon->refindid = currcon->conindid;
12621 newcon->conid = currcon->oid;
12622 newcon->qual = (Node *) fkconstraint;
12623
12624 /* Find or create work queue entry for this table */
12625 tab = ATGetQueueEntry(wqueue, rel);
12626 tab->constraints = lappend(tab->constraints, newcon);
12627 }
12628
12629 /*
12630 * If the table at either end of the constraint is partitioned, we
12631 * need to recurse and create triggers for each constraint that is a
12632 * child of this one.
12633 */
12634 if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
12638 lockmode,
12643 }
12644
12645 table_close(rel, NoLock);
12646
12647 return changed;
12648}
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 20755 of file tablecmds.c.

20757{
20759 catalog;
20762 SysScanDesc scan;
20764 AttrNumber attno;
20765 int natts;
20767 ObjectAddress address;
20768 const char *trigger_name;
20772 ParseState *pstate = make_parsestate(NULL);
20773
20774 pstate->p_sourcetext = context->queryString;
20775
20776 /*
20777 * We must lock the default partition if one exists, because attaching a
20778 * new partition will change its partition constraint.
20779 */
20784
20786
20787 /*
20788 * XXX I think it'd be a good idea to grab locks on all tables referenced
20789 * by FKs at this point also.
20790 */
20791
20792 /*
20793 * Must be owner of both parent and source table -- parent was checked by
20794 * ATSimplePermissions call in ATPrepCmd
20795 */
20798
20799 /* A partition can only have one parent */
20800 if (attachrel->rd_rel->relispartition)
20801 ereport(ERROR,
20803 errmsg("\"%s\" is already a partition",
20805
20806 if (OidIsValid(attachrel->rd_rel->reloftype))
20807 ereport(ERROR,
20809 errmsg("cannot attach a typed table as partition")));
20810
20811 /*
20812 * Disallow attaching a partition if the table is referenced in a
20813 * publication EXCEPT clause. Changing the partition hierarchy could alter
20814 * the effective publication membership.
20815 */
20817 if (exceptpuboids != NIL)
20818 {
20819 bool first = true;
20821
20823
20825 {
20826 char *pubname = get_publication_name(pubid, false);
20827
20828 if (first)
20829 appendStringInfo(&pubnames, _("\"%s\""), pubname);
20830 else
20831 appendStringInfo(&pubnames, _(", \"%s\""), pubname);
20832 first = false;
20833 }
20834
20835 ereport(ERROR,
20837 errmsg_plural("cannot attach table \"%s\" as partition because it is referenced in publication %s EXCEPT clause",
20838 "cannot attach table \"%s\" as partition because it is referenced in publications %s EXCEPT clause",
20841 pubnames.data),
20842 errdetail("The publication EXCEPT clause cannot contain tables that are partitions."),
20843 errhint("Change the publication's EXCEPT clause using ALTER PUBLICATION ... SET ALL TABLES."));
20844 }
20845
20847
20848 /*
20849 * Table being attached should not already be part of inheritance; either
20850 * as a child table...
20851 */
20858 NULL, 1, &skey);
20860 ereport(ERROR,
20862 errmsg("cannot attach inheritance child as partition")));
20863 systable_endscan(scan);
20864
20865 /* ...or as a parent table (except the case when it is partitioned) */
20871 1, &skey);
20873 attachrel->rd_rel->relkind == RELKIND_RELATION)
20874 ereport(ERROR,
20876 errmsg("cannot attach inheritance parent as partition")));
20877 systable_endscan(scan);
20879
20880 /*
20881 * Prevent circularity by seeing if rel is a partition of attachrel. (In
20882 * particular, this disallows making a rel a partition of itself.)
20883 *
20884 * We do that by checking if rel is a member of the list of attachrel's
20885 * partitions provided the latter is partitioned at all. We want to avoid
20886 * having to construct this list again, so we request the strongest lock
20887 * on all partitions. We need the strongest lock, because we may decide
20888 * to scan them if we find out that the table being attached (or its leaf
20889 * partitions) may contain rows that violate the partition constraint. If
20890 * the table has a constraint that would prevent such rows, which by
20891 * definition is present in all the partitions, we need not scan the
20892 * table, nor its partitions. But we cannot risk a deadlock by taking a
20893 * weaker lock now and the stronger one only when needed.
20894 */
20898 ereport(ERROR,
20900 errmsg("circular inheritance not allowed"),
20901 errdetail("\"%s\" is already a child of \"%s\".",
20904
20905 /* If the parent is permanent, so must be all of its partitions. */
20906 if (rel->rd_rel->relpersistence != RELPERSISTENCE_TEMP &&
20907 attachrel->rd_rel->relpersistence == RELPERSISTENCE_TEMP)
20908 ereport(ERROR,
20910 errmsg("cannot attach a temporary relation as partition of permanent relation \"%s\"",
20912
20913 /* Temp parent cannot have a partition that is itself not a temp */
20914 if (rel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
20915 attachrel->rd_rel->relpersistence != RELPERSISTENCE_TEMP)
20916 ereport(ERROR,
20918 errmsg("cannot attach a permanent relation as partition of temporary relation \"%s\"",
20920
20921 /* If the parent is temp, it must belong to this session */
20922 if (RELATION_IS_OTHER_TEMP(rel))
20923 ereport(ERROR,
20925 errmsg("cannot attach as partition of temporary relation of another session")));
20926
20927 /* Ditto for the partition */
20929 ereport(ERROR,
20931 errmsg("cannot attach temporary relation of another session as partition")));
20932
20933 /*
20934 * Check if attachrel has any identity columns or any columns that aren't
20935 * in the parent.
20936 */
20938 natts = tupleDesc->natts;
20939 for (attno = 1; attno <= natts; attno++)
20940 {
20942 char *attributeName = NameStr(attribute->attname);
20943
20944 /* Ignore dropped */
20945 if (attribute->attisdropped)
20946 continue;
20947
20948 if (attribute->attidentity)
20949 ereport(ERROR,
20951 errmsg("table \"%s\" being attached contains an identity column \"%s\"",
20953 errdetail("The new partition may not contain an identity column."));
20954
20955 /* Try to find the column in parent (matching on column name) */
20959 ereport(ERROR,
20961 errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
20964 errdetail("The new partition may contain only the columns present in parent.")));
20965 }
20966
20967 /*
20968 * If child_rel has row-level triggers with transition tables, we
20969 * currently don't allow it to become a partition. See also prohibitions
20970 * in ATExecAddInherit() and CreateTrigger().
20971 */
20973 if (trigger_name != NULL)
20974 ereport(ERROR,
20976 errmsg("trigger \"%s\" prevents table \"%s\" from becoming a partition",
20978 errdetail("ROW triggers with transition tables are not supported on partitions.")));
20979
20980 /*
20981 * Check that the new partition's bound is valid and does not overlap any
20982 * of existing partitions of the parent - note that it does not return on
20983 * error.
20984 */
20986 cmd->bound, pstate);
20987
20989
20990 /*
20991 * Generate a partition constraint from the partition bound specification.
20992 * If the parent itself is a partition, make sure to include its
20993 * constraint as well.
20994 */
20996
20997 /*
20998 * Use list_concat_copy() to avoid modifying partBoundConstraint in place,
20999 * since it's needed later to construct the constraint expression for
21000 * validating against the default partition, if any.
21001 */
21004
21005 /* Skip validation if there are no constraints to validate. */
21006 if (partConstraint)
21007 {
21008 /*
21009 * Run the partition quals through const-simplification similar to
21010 * check constraints. We skip canonicalize_qual, though, because
21011 * partition quals should be in canonical form already.
21012 */
21015 (Node *) partConstraint);
21016
21017 /* XXX this sure looks wrong */
21019
21020 /*
21021 * Adjust the generated constraint to match this partition's attribute
21022 * numbers.
21023 */
21025 rel);
21026
21027 /* Validate partition constraints against the table being attached. */
21029 false);
21030 }
21031
21032 /*
21033 * If we're attaching a partition other than the default partition and a
21034 * default one exists, then that partition's partition constraint changes,
21035 * so add an entry to the work queue to validate it, too. (We must not do
21036 * this when the partition being attached is the default one; we already
21037 * did it above!)
21038 */
21040 {
21043
21044 Assert(!cmd->bound->is_default);
21045
21046 /* we already hold a lock on the default partition */
21050
21051 /*
21052 * Map the Vars in the constraint expression from rel's attnos to
21053 * defaultrel's.
21054 */
21057 1, defaultrel, rel);
21059 defPartConstraint, true);
21060
21061 /* keep our lock until commit. */
21063 }
21064
21066
21067 /*
21068 * If the partition we just attached is partitioned itself, invalidate
21069 * relcache for all descendent partitions too to ensure that their
21070 * rd_partcheck expression trees are rebuilt; partitions already locked at
21071 * the beginning of this function.
21072 */
21073 if (attachrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21074 {
21075 ListCell *l;
21076
21077 foreach(l, attachrel_children)
21078 {
21080 }
21081 }
21082
21083 /* keep our lock until commit */
21085
21086 return address;
21087}
Node * eval_const_expressions(PlannerInfo *root, Node *node)
Definition clauses.c:2500
#define _(x)
Definition elog.c:96
int int int errmsg_plural(const char *fmt_singular, const char *fmt_plural, unsigned long n,...) pg_attribute_printf(1
List * list_concat_copy(const List *list1, const List *list2)
Definition list.c:598
char * get_publication_name(Oid pubid, bool missing_ok)
Definition lsyscache.c:3998
Expr * make_ands_explicit(List *andclauses)
Definition makefuncs.c:799
ParseState * make_parsestate(ParseState *parentParseState)
Definition parse_node.c:39
void check_new_partition_bound(char *relname, Relation parent, PartitionBoundSpec *spec, ParseState *pstate)
List * get_qual_from_partbound(Relation parent, PartitionBoundSpec *spec)
Definition partbounds.c:250
List * RelationGetPartitionQual(Relation rel)
Definition partcache.c:277
Oid get_default_oid_from_partdesc(PartitionDesc partdesc)
Definition partdesc.c:501
List * map_partition_varattnos(List *expr, int fromrel_varno, Relation to_rel, Relation from_rel)
Definition partition.c:222
List * get_proposed_default_constraint(List *new_part_constraints)
Definition partition.c:370
List * GetRelationExcludedPublications(Oid relid)
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition stringinfo.c:145
void 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 22106 of file tablecmds.c.

22107{
22110 Relation parentTbl;
22111 ObjectAddress address;
22112 Oid partIdxId;
22115
22116 /*
22117 * We need to obtain lock on the index 'name' to modify it, but we also
22118 * need to read its owning table's tuple descriptor -- so we need to lock
22119 * both. To avoid deadlocks, obtain lock on the table before doing so on
22120 * the index. Furthermore, we need to examine the parent table of the
22121 * partition, so lock that one too.
22122 */
22123 state.partitionOid = InvalidOid;
22124 state.parentTblOid = parentIdx->rd_index->indrelid;
22125 state.lockedParentTbl = false;
22126 partIdxId =
22129 &state);
22130 /* Not there? */
22131 if (!OidIsValid(partIdxId))
22132 ereport(ERROR,
22134 errmsg("index \"%s\" does not exist", name->relname)));
22135
22136 /* no deadlock risk: RangeVarGetRelidExtended already acquired the lock */
22138
22139 /* we already hold locks on both tables, so this is safe: */
22140 parentTbl = relation_open(parentIdx->rd_index->indrelid, AccessShareLock);
22141 partTbl = relation_open(partIdx->rd_index->indrelid, NoLock);
22142
22144
22145 /*
22146 * Check if the index is already attached to the correct parent,
22147 * ultimately attempting one round of validation if already the case.
22148 */
22149 currParent = partIdx->rd_rel->relispartition ?
22151 if (currParent != RelationGetRelid(parentIdx))
22152 {
22155 AttrMap *attmap;
22156 bool found;
22157 int i;
22161
22162 /*
22163 * If this partition already has an index attached, refuse the
22164 * operation.
22165 */
22167
22169 ereport(ERROR,
22171 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22173 RelationGetRelationName(parentIdx)),
22174 errdetail("Index \"%s\" is already attached to another index.",
22176
22177 /* Make sure it indexes a partition of the other index's table */
22178 partDesc = RelationGetPartitionDesc(parentTbl, true);
22179 found = false;
22180 for (i = 0; i < partDesc->nparts; i++)
22181 {
22182 if (partDesc->oids[i] == state.partitionOid)
22183 {
22184 found = true;
22185 break;
22186 }
22187 }
22188 if (!found)
22189 ereport(ERROR,
22191 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22193 RelationGetRelationName(parentIdx)),
22194 errdetail("Index \"%s\" is not an index on any partition of table \"%s\".",
22196 RelationGetRelationName(parentTbl))));
22197
22198 /* Ensure the indexes are compatible */
22200 parentInfo = BuildIndexInfo(parentIdx);
22202 RelationGetDescr(parentTbl),
22203 false);
22205 partIdx->rd_indcollation,
22206 parentIdx->rd_indcollation,
22207 partIdx->rd_opfamily,
22208 parentIdx->rd_opfamily,
22209 attmap))
22210 ereport(ERROR,
22212 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22214 RelationGetRelationName(parentIdx)),
22215 errdetail("The index definitions do not match.")));
22216
22217 /*
22218 * If there is a constraint in the parent, make sure there is one in
22219 * the child too.
22220 */
22222 RelationGetRelid(parentIdx));
22223
22225 {
22227 partIdxId);
22228 if (!OidIsValid(cldConstrId))
22229 ereport(ERROR,
22231 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22233 RelationGetRelationName(parentIdx)),
22234 errdetail("The index \"%s\" belongs to a constraint in table \"%s\" but no constraint exists for index \"%s\".",
22235 RelationGetRelationName(parentIdx),
22236 RelationGetRelationName(parentTbl),
22238 }
22239
22240 /*
22241 * If it's a primary key, make sure the columns in the partition are
22242 * NOT NULL.
22243 */
22244 if (parentIdx->rd_index->indisprimary)
22246
22247 /* All good -- do it */
22252
22254
22255 validatePartitionedIndex(parentIdx, parentTbl);
22256 }
22257 else if (!parentIdx->rd_index->indisvalid)
22258 {
22259 /*
22260 * The index is attached, but the parent is still invalid; see if it
22261 * can be validated now.
22262 */
22263 validatePartitionedIndex(parentIdx, parentTbl);
22264 }
22265
22266 relation_close(parentTbl, AccessShareLock);
22267 /* keep these locks till commit */
22270
22271 return address;
22272}
bool CompareIndexInfo(const IndexInfo *info1, const IndexInfo *info2, const Oid *collations1, const Oid *collations2, const Oid *opfamilies1, const Oid *opfamilies2, const AttrMap *attmap)
Definition index.c:2555
void IndexSetParentIndex(Relation partitionIdx, Oid parentOid)
Definition indexcmds.c:4476
Oid get_relation_idx_constraint_oid(Oid relationId, Oid indexId)
void ConstraintSetParentConstraint(Oid childConstrId, Oid parentConstrId, Oid childTableId)
Form_pg_index rd_index
Definition rel.h:192
Oid * rd_opfamily
Definition rel.h:207
Oid * rd_indcollation
Definition rel.h:217
static void RangeVarCallbackForAttachIndex(const RangeVar *rv, Oid relOid, Oid oldRelOid, void *arg)
static void validatePartitionedIndex(Relation partedIdx, Relation partedTbl)
static void verifyPartitionIndexNotNull(IndexInfo *iinfo, Relation partition)
static void refuseDupeIndexAttach(Relation parentIdx, Relation partIdx, Relation partitionTbl)
const char * name

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

Referenced by ATExecCmd().

◆ ATExecChangeOwner()

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

Definition at line 16546 of file tablecmds.c.

16547{
16550 HeapTuple tuple;
16552
16553 /*
16554 * Get exclusive lock till end of transaction on the target table. Use
16555 * relation_open so that we can work on indexes and sequences.
16556 */
16557 target_rel = relation_open(relationOid, lockmode);
16558
16559 /* Get its pg_class tuple, too */
16561
16562 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid));
16563 if (!HeapTupleIsValid(tuple))
16564 elog(ERROR, "cache lookup failed for relation %u", relationOid);
16566
16567 /* Can we change the ownership of this tuple? */
16568 switch (tuple_class->relkind)
16569 {
16570 case RELKIND_RELATION:
16571 case RELKIND_VIEW:
16572 case RELKIND_MATVIEW:
16575 case RELKIND_PROPGRAPH:
16576 /* ok to change owner */
16577 break;
16578 case RELKIND_INDEX:
16579 if (!recursing)
16580 {
16581 /*
16582 * Because ALTER INDEX OWNER used to be allowed, and in fact
16583 * is generated by old versions of pg_dump, we give a warning
16584 * and do nothing rather than erroring out. Also, to avoid
16585 * unnecessary chatter while restoring those old dumps, say
16586 * nothing at all if the command would be a no-op anyway.
16587 */
16588 if (tuple_class->relowner != newOwnerId)
16591 errmsg("cannot change owner of index \"%s\"",
16592 NameStr(tuple_class->relname)),
16593 errhint("Change the ownership of the index's table instead.")));
16594 /* quick hack to exit via the no-op path */
16595 newOwnerId = tuple_class->relowner;
16596 }
16597 break;
16599 if (recursing)
16600 break;
16601 ereport(ERROR,
16603 errmsg("cannot change owner of index \"%s\"",
16604 NameStr(tuple_class->relname)),
16605 errhint("Change the ownership of the index's table instead.")));
16606 break;
16607 case RELKIND_SEQUENCE:
16608 if (!recursing &&
16609 tuple_class->relowner != newOwnerId)
16610 {
16611 /* if it's an owned sequence, disallow changing it by itself */
16612 Oid tableId;
16613 int32 colId;
16614
16615 if (sequenceIsOwned(relationOid, DEPENDENCY_AUTO, &tableId, &colId) ||
16617 ereport(ERROR,
16619 errmsg("cannot change owner of sequence \"%s\"",
16620 NameStr(tuple_class->relname)),
16621 errdetail("Sequence \"%s\" is linked to table \"%s\".",
16622 NameStr(tuple_class->relname),
16624 }
16625 break;
16627 if (recursing)
16628 break;
16629 ereport(ERROR,
16631 errmsg("\"%s\" is a composite type",
16632 NameStr(tuple_class->relname)),
16633 /* translator: %s is an SQL ALTER command */
16634 errhint("Use %s instead.",
16635 "ALTER TYPE")));
16636 break;
16637 case RELKIND_TOASTVALUE:
16638 if (recursing)
16639 break;
16641 default:
16642 ereport(ERROR,
16644 errmsg("cannot change owner of relation \"%s\"",
16645 NameStr(tuple_class->relname)),
16647 }
16648
16649 /*
16650 * If the new owner is the same as the existing owner, consider the
16651 * command to have succeeded. This is for dump restoration purposes.
16652 */
16653 if (tuple_class->relowner != newOwnerId)
16654 {
16658 Acl *newAcl;
16660 bool isNull;
16661 HeapTuple newtuple;
16662
16663 /* skip permission checks when recursing to index or toast table */
16664 if (!recursing)
16665 {
16666 /* Superusers can always do it */
16667 if (!superuser())
16668 {
16669 Oid namespaceOid = tuple_class->relnamespace;
16671
16672 /* Otherwise, must be owner of the existing object */
16673 if (!object_ownercheck(RelationRelationId, relationOid, GetUserId()))
16676
16677 /* Must be able to become new owner */
16679
16680 /* New owner must have CREATE privilege on namespace */
16682 ACL_CREATE);
16683 if (aclresult != ACLCHECK_OK)
16686 }
16687 }
16688
16689 memset(repl_null, false, sizeof(repl_null));
16690 memset(repl_repl, false, sizeof(repl_repl));
16691
16694
16695 /*
16696 * Determine the modified ACL for the new owner. This is only
16697 * necessary when the ACL is non-null.
16698 */
16701 &isNull);
16702 if (!isNull)
16703 {
16705 tuple_class->relowner, newOwnerId);
16706 repl_repl[Anum_pg_class_relacl - 1] = true;
16708 }
16709
16711
16712 CatalogTupleUpdate(class_rel, &newtuple->t_self, newtuple);
16713
16714 heap_freetuple(newtuple);
16715
16716 /*
16717 * We must similarly update any per-column ACLs to reflect the new
16718 * owner; for neatness reasons that's split out as a subroutine.
16719 */
16720 change_owner_fix_column_acls(relationOid,
16721 tuple_class->relowner,
16722 newOwnerId);
16723
16724 /*
16725 * Update owner dependency reference, if any. A composite type has
16726 * none, because it's tracked for the pg_type entry instead of here;
16727 * indexes and TOAST tables don't have their own entries either.
16728 */
16729 if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE &&
16730 tuple_class->relkind != RELKIND_INDEX &&
16732 tuple_class->relkind != RELKIND_TOASTVALUE)
16734 newOwnerId);
16735
16736 /*
16737 * Also change the ownership of the table's row type, if it has one
16738 */
16739 if (OidIsValid(tuple_class->reltype))
16741
16742 /*
16743 * If we are operating on a table or materialized view, also change
16744 * the ownership of any indexes and sequences that belong to the
16745 * relation, as well as its toast table (if it has one).
16746 */
16747 if (tuple_class->relkind == RELKIND_RELATION ||
16749 tuple_class->relkind == RELKIND_MATVIEW ||
16750 tuple_class->relkind == RELKIND_TOASTVALUE)
16751 {
16753 ListCell *i;
16754
16755 /* Find all the indexes belonging to this relation */
16757
16758 /* For each index, recursively change its ownership */
16759 foreach(i, index_oid_list)
16760 ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode);
16761
16763 }
16764
16765 /* If it has a toast table, recurse to change its ownership */
16766 if (tuple_class->reltoastrelid != InvalidOid)
16768 true, lockmode);
16769
16770 /* If it has dependent sequences, recurse to change them too */
16771 change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode);
16772 }
16773
16775
16776 ReleaseSysCache(tuple);
16779}
Acl * aclnewowner(const Acl *old_acl, Oid oldOwnerId, Oid newOwnerId)
Definition acl.c:1147
void check_can_set_role(Oid member, Oid role)
Definition acl.c:5371
#define DatumGetAclP(X)
Definition acl.h:120
#define pg_fallthrough
Definition c.h:217
#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:4027

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

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

◆ ATExecClusterOn()

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

Definition at line 16922 of file tablecmds.c.

16923{
16924 Oid indexOid;
16925 ObjectAddress address;
16926
16927 indexOid = get_relname_relid(indexName, rel->rd_rel->relnamespace);
16928
16929 if (!OidIsValid(indexOid))
16930 ereport(ERROR,
16932 errmsg("index \"%s\" for table \"%s\" does not exist",
16934
16935 /* Check index is valid to cluster on */
16936 check_index_is_clusterable(rel, indexOid, lockmode);
16937
16938 /* And do the work */
16939 mark_index_clustered(rel, indexOid, false);
16940
16941 ObjectAddressSet(address,
16942 RelationRelationId, indexOid);
16943
16944 return address;
16945}
void check_index_is_clusterable(Relation OldHeap, Oid indexOid, LOCKMODE lockmode)
Definition repack.c:769
void mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Definition repack.c:829

References check_index_is_clusterable(), ereport, errcode(), errmsg, ERROR, fb(), get_relname_relid(), mark_index_clustered(), ObjectAddressSet, OidIsValid, RelationData::rd_rel, and RelationGetRelationName.

Referenced by ATExecCmd().

◆ ATExecCmd()

static void ATExecCmd ( List **  wqueue,
AlteredTableInfo tab,
AlterTableCmd cmd,
LOCKMODE  lockmode,
AlterTablePass  cur_pass,
AlterTableUtilityContext context 
)
static

Definition at line 5451 of file tablecmds.c.

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

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

Referenced by ATRewriteCatalogs().

◆ ATExecColumnDefault()

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

Definition at line 8223 of file tablecmds.c.

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

References AddRelationNewConstraints(), attnum, DROP_RESTRICT, ereport, errcode(), errhint(), errmsg, ERROR, fb(), get_attnum(), InvalidAttrNumber, list_make1, NIL, ObjectAddressSubSet, palloc_object, RelationGetDescr, RelationGetRelationName, RelationGetRelid, RemoveAttrDefault(), and TupleDescAttr().

Referenced by ATExecCmd().

◆ ATExecCookedColumnDefault()

static ObjectAddress ATExecCookedColumnDefault ( Relation  rel,
AttrNumber  attnum,
Node newDefault 
)
static

Definition at line 8308 of file tablecmds.c.

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

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

21440{
21441 Relation partRel;
21442 ObjectAddress address;
21444
21445 /*
21446 * We must lock the default partition, because detaching this partition
21447 * will change its partition constraint.
21448 */
21452 {
21453 /*
21454 * Concurrent detaching when a default partition exists is not
21455 * supported. The main problem is that the default partition
21456 * constraint would change. And there's a definitional problem: what
21457 * should happen to the tuples that are being inserted that belong to
21458 * the partition being detached? Putting them on the partition being
21459 * detached would be wrong, since they'd become "lost" after the
21460 * detaching completes but we cannot put them in the default partition
21461 * either until we alter its partition constraint.
21462 *
21463 * I think we could solve this problem if we effected the constraint
21464 * change before committing the first transaction. But the lock would
21465 * have to remain AEL and it would cause concurrent query planning to
21466 * be blocked, so changing it that way would be even worse.
21467 */
21468 if (concurrent)
21469 ereport(ERROR,
21471 errmsg("cannot detach partitions concurrently when a default partition exists")));
21473 }
21474
21475 /*
21476 * In concurrent mode, the partition is locked with share-update-exclusive
21477 * in the first transaction. This allows concurrent transactions to be
21478 * doing DML to the partition.
21479 */
21480 partRel = table_openrv(name, concurrent ? ShareUpdateExclusiveLock :
21482
21483 /*
21484 * Check inheritance conditions and either delete the pg_inherits row (in
21485 * non-concurrent mode) or just set the inhdetachpending flag.
21486 */
21487 if (!concurrent)
21488 RemoveInheritance(partRel, rel, false);
21489 else
21490 MarkInheritDetached(partRel, rel);
21491
21492 /*
21493 * Ensure that foreign keys still hold after this detach. This keeps
21494 * locks on the referencing tables, which prevents concurrent transactions
21495 * from adding rows that we wouldn't see. For this to work in concurrent
21496 * mode, it is critical that the partition appears as no longer attached
21497 * for the RI queries as soon as the first transaction commits.
21498 */
21500
21501 /*
21502 * Concurrent mode has to work harder; first we add a new constraint to
21503 * the partition that matches the partition constraint. Then we close our
21504 * existing transaction, and in a new one wait for all processes to catch
21505 * up on the catalog updates we've done so far; at that point we can
21506 * complete the operation.
21507 */
21508 if (concurrent)
21509 {
21510 Oid partrelid,
21512 LOCKTAG tag;
21513 char *parentrelname;
21514 char *partrelname;
21515
21516 /*
21517 * We're almost done now; the only traces that remain are the
21518 * pg_inherits tuple and the partition's relpartbounds. Before we can
21519 * remove those, we need to wait until all transactions that know that
21520 * this is a partition are gone.
21521 */
21522
21523 /*
21524 * Remember relation OIDs to re-acquire them later; and relation names
21525 * too, for error messages if something is dropped in between.
21526 */
21527 partrelid = RelationGetRelid(partRel);
21532 RelationGetRelationName(partRel));
21533
21534 /* Invalidate relcache entries for the parent -- must be before close */
21536
21537 table_close(partRel, NoLock);
21538 table_close(rel, NoLock);
21539 tab->rel = NULL;
21540
21541 /* Make updated catalog entry visible */
21544
21546
21547 /*
21548 * Now wait. This ensures that all queries that were planned
21549 * including the partition are finished before we remove the rest of
21550 * catalog entries. We don't need or indeed want to acquire this
21551 * lock, though -- that would block later queries.
21552 *
21553 * We don't need to concern ourselves with waiting for a lock on the
21554 * partition itself, since we will acquire AccessExclusiveLock below.
21555 */
21558
21559 /*
21560 * Now acquire locks in both relations again. Note they may have been
21561 * removed in the meantime, so care is required.
21562 */
21565
21566 /* If the relations aren't there, something bad happened; bail out */
21567 if (rel == NULL)
21568 {
21569 if (partRel != NULL) /* shouldn't happen */
21570 elog(WARNING, "dangling partition \"%s\" remains, can't fix",
21571 partrelname);
21572 ereport(ERROR,
21574 errmsg("partitioned table \"%s\" was removed concurrently",
21575 parentrelname)));
21576 }
21577 if (partRel == NULL)
21578 ereport(ERROR,
21580 errmsg("partition \"%s\" was removed concurrently", partrelname)));
21581
21582 tab->rel = rel;
21583 }
21584
21585 /*
21586 * Detaching the partition might involve TOAST table access, so ensure we
21587 * have a valid snapshot.
21588 */
21590
21591 /* Do the final part of detaching */
21592 DetachPartitionFinalize(rel, partRel, concurrent, defaultPartOid);
21593
21595
21597
21598 /* keep our lock until commit */
21599 table_close(partRel, NoLock);
21600
21601 return address;
21602}
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 21945 of file tablecmds.c.

21946{
21947 Relation partRel;
21948 ObjectAddress address;
21950
21952
21953 /*
21954 * Wait until existing snapshots are gone. This is important if the
21955 * second transaction of DETACH PARTITION CONCURRENTLY is canceled: the
21956 * user could immediately run DETACH FINALIZE without actually waiting for
21957 * existing transactions. We must not complete the detach action until
21958 * all such queries are complete (otherwise we would present them with an
21959 * inconsistent view of catalogs).
21960 */
21961 WaitForOlderSnapshots(snap->xmin, false);
21962
21963 DetachPartitionFinalize(rel, partRel, true, InvalidOid);
21964
21966
21967 table_close(partRel, NoLock);
21968
21969 return address;
21970}
void WaitForOlderSnapshots(TransactionId limitXmin, bool progress)
Definition indexcmds.c:436
Snapshot GetActiveSnapshot(void)
Definition snapmgr.c:800

References AccessExclusiveLock, DetachPartitionFinalize(), fb(), GetActiveSnapshot(), InvalidOid, name, NoLock, ObjectAddressSet, RelationGetRelid, table_close(), table_openrv(), and WaitForOlderSnapshots().

Referenced by ATExecCmd().

◆ ATExecDropCluster()

static void ATExecDropCluster ( Relation  rel,
LOCKMODE  lockmode 
)
static

Definition at line 16954 of file tablecmds.c.

16955{
16956 mark_index_clustered(rel, InvalidOid, false);
16957}

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

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

References add_exact_object_address(), Assert, AT_DropColumn, ATExecDropColumn(), ATSimplePermissions(), ATT_FOREIGN_TABLE, ATT_PARTITIONED_TABLE, ATT_TABLE, attnum, bms_make_singleton(), CatalogTupleUpdate(), check_stack_depth(), CheckAlterTableIsSafe(), CommandCounterIncrement(), elog, ereport, errcode(), errhint(), errmsg, ERROR, fb(), find_inheritance_children(), FirstLowInvalidHeapAttributeNumber, free_object_addresses(), GETSTRUCT(), has_partition_attrs(), heap_freetuple(), HeapTupleIsValid, InvalidObjectAddress, lfirst_oid, new_object_addresses(), NoLock, NOTICE, performMultipleDeletions(), RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RowExclusiveLock, SearchSysCacheAttName(), SearchSysCacheCopyAttName(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by ATExecCmd(), and ATExecDropColumn().

◆ ATExecDropConstraint()

static void ATExecDropConstraint ( Relation  rel,
const char constrName,
DropBehavior  behavior,
bool  recurse,
bool  missing_ok,
LOCKMODE  lockmode 
)
static

Definition at line 14486 of file tablecmds.c.

14489{
14491 SysScanDesc scan;
14492 ScanKeyData skey[3];
14493 HeapTuple tuple;
14494 bool found = false;
14495
14497
14498 /*
14499 * Find and drop the target constraint
14500 */
14501 ScanKeyInit(&skey[0],
14505 ScanKeyInit(&skey[1],
14509 ScanKeyInit(&skey[2],
14514 true, NULL, 3, skey);
14515
14516 /* There can be at most one matching row */
14517 if (HeapTupleIsValid(tuple = systable_getnext(scan)))
14518 {
14519 dropconstraint_internal(rel, tuple, behavior, recurse, false,
14520 missing_ok, lockmode);
14521 found = true;
14522 }
14523
14524 systable_endscan(scan);
14525
14526 if (!found)
14527 {
14528 if (!missing_ok)
14529 ereport(ERROR,
14531 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14533 else
14535 errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
14537 }
14538
14540}
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 8886 of file tablecmds.c.

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

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

8587{
8588 HeapTuple tuple;
8592 ObjectAddress address;
8593 Oid seqid;
8595 bool ispartitioned;
8596
8597 ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
8598 if (ispartitioned && !recurse)
8599 ereport(ERROR,
8601 errmsg("cannot drop identity from a column of only the partitioned table"),
8602 errhint("Do not specify the ONLY keyword.")));
8603
8604 if (rel->rd_rel->relispartition && !recursing)
8605 ereport(ERROR,
8607 errmsg("cannot drop identity from a column of a partition"));
8608
8611 if (!HeapTupleIsValid(tuple))
8612 ereport(ERROR,
8614 errmsg("column \"%s\" of relation \"%s\" does not exist",
8616
8618 attnum = attTup->attnum;
8619
8620 if (attnum <= 0)
8621 ereport(ERROR,
8623 errmsg("cannot alter system column \"%s\"",
8624 colName)));
8625
8626 if (!attTup->attidentity)
8627 {
8628 if (!missing_ok)
8629 ereport(ERROR,
8631 errmsg("column \"%s\" of relation \"%s\" is not an identity column",
8633 else
8634 {
8636 (errmsg("column \"%s\" of relation \"%s\" is not an identity column, skipping",
8638 heap_freetuple(tuple);
8640 return InvalidObjectAddress;
8641 }
8642 }
8643
8644 attTup->attidentity = '\0';
8645 CatalogTupleUpdate(attrelation, &tuple->t_self, tuple);
8646
8648 RelationGetRelid(rel),
8649 attTup->attnum);
8651 RelationGetRelid(rel), attnum);
8652 heap_freetuple(tuple);
8653
8655
8656 /*
8657 * Recurse to drop the identity from column in partitions. Identity is
8658 * not inherited in regular inheritance children so ignore them.
8659 */
8660 if (recurse && ispartitioned)
8661 {
8662 List *children;
8663 ListCell *lc;
8664
8665 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
8666
8667 foreach(lc, children)
8668 {
8670
8672 ATExecDropIdentity(childrel, colName, false, lockmode, recurse, true);
8674 }
8675 }
8676
8677 if (!recursing)
8678 {
8679 /* drop the internal sequence */
8680 seqid = getIdentitySequence(rel, attnum, false);
8685 seqaddress.objectId = seqid;
8686 seqaddress.objectSubId = 0;
8688 }
8689
8690 return address;
8691}
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 18296 of file tablecmds.c.

18297{
18298 ObjectAddress address;
18300
18301 /*
18302 * AccessShareLock on the parent is probably enough, seeing that DROP
18303 * TABLE doesn't lock parent tables at all. We need some lock since we'll
18304 * be inspecting the parent's schema.
18305 */
18307
18308 /*
18309 * We don't bother to check ownership of the parent table --- ownership of
18310 * the child is presumed enough rights.
18311 */
18312
18313 /* Off to RemoveInheritance() where most of the work happens */
18314 RemoveInheritance(rel, parent_rel, false);
18315
18318
18319 /* keep our lock on the parent relation until commit */
18321
18322 return address;
18323}

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

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

18825{
18826 Oid relid = RelationGetRelid(rel);
18828 HeapTuple tuple;
18829
18830 if (!OidIsValid(rel->rd_rel->reloftype))
18831 ereport(ERROR,
18833 errmsg("\"%s\" is not a typed table",
18835
18836 /*
18837 * We don't bother to check ownership of the type --- ownership of the
18838 * table is presumed enough rights. No lock required on the type, either.
18839 */
18840
18841 drop_parent_dependency(relid, TypeRelationId, rel->rd_rel->reloftype,
18843
18844 /* Clear pg_class.reloftype */
18847 if (!HeapTupleIsValid(tuple))
18848 elog(ERROR, "cache lookup failed for relation %u", relid);
18849 ((Form_pg_class) GETSTRUCT(tuple))->reloftype = InvalidOid;
18851
18853
18854 heap_freetuple(tuple);
18856}

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

17699{
17700 EnableDisableRule(rel, rulename, fires_when);
17701
17703 RelationGetRelid(rel), 0);
17704}
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 17679 of file tablecmds.c.

17682{
17683 EnableDisableTrigger(rel, trigname, InvalidOid,
17684 fires_when, skip_system, recurse,
17685 lockmode);
17686
17688 RelationGetRelid(rel), 0);
17689}
void EnableDisableTrigger(Relation rel, const char *tgname, Oid tgparent, char fires_when, bool skip_system, bool recurse, LOCKMODE lockmode)
Definition trigger.c:1728

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

Referenced by ATExecCmd().

◆ ATExecForceNoForceRowSecurity()

static void ATExecForceNoForceRowSecurity ( Relation  rel,
bool  force_rls 
)
static

Definition at line 19100 of file tablecmds.c.

19101{
19103 Oid relid;
19104 HeapTuple tuple;
19105
19106 relid = RelationGetRelid(rel);
19107
19109
19111
19112 if (!HeapTupleIsValid(tuple))
19113 elog(ERROR, "cache lookup failed for relation %u", relid);
19114
19116 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
19117
19119 RelationGetRelid(rel), 0);
19120
19122 heap_freetuple(tuple);
19123}

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

19130{
19132 ForeignServer *server;
19134 HeapTuple tuple;
19135 bool isnull;
19139 Datum datum;
19141
19142 if (options == NIL)
19143 return;
19144
19146
19148 ObjectIdGetDatum(rel->rd_id));
19149 if (!HeapTupleIsValid(tuple))
19150 ereport(ERROR,
19152 errmsg("foreign table \"%s\" does not exist",
19155 server = GetForeignServer(tableform->ftserver);
19156 fdw = GetForeignDataWrapper(server->fdwid);
19157
19158 memset(repl_val, 0, sizeof(repl_val));
19159 memset(repl_null, false, sizeof(repl_null));
19160 memset(repl_repl, false, sizeof(repl_repl));
19161
19162 /* Extract the current options */
19164 tuple,
19166 &isnull);
19167 if (isnull)
19168 datum = PointerGetDatum(NULL);
19169
19170 /* Transform the options */
19172 datum,
19173 options,
19174 fdw->fdwvalidator);
19175
19176 if (DatumGetPointer(datum) != NULL)
19178 else
19180
19182
19183 /* Everything looks good - update the tuple */
19184
19187
19188 CatalogTupleUpdate(ftrel, &tuple->t_self, tuple);
19189
19190 /*
19191 * Invalidate relcache so that all sessions will refresh any cached plans
19192 * that might depend on the old options.
19193 */
19195
19197 RelationGetRelid(rel), 0);
19198
19200
19201 heap_freetuple(tuple);
19202}

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

23497{
23500 List *extDepState = NIL;
23503 Oid ownerId = InvalidOid;
23504 Oid save_userid;
23505 int save_sec_context;
23506 int save_nestlevel;
23507
23508 /*
23509 * Check ownership of merged partitions - partitions with different owners
23510 * cannot be merged. Also, collect the OIDs of these partitions during the
23511 * check.
23512 */
23514 {
23516
23517 /*
23518 * We are going to detach and remove this partition. We already took
23519 * AccessExclusiveLock lock on transformPartitionCmdForMerge, so here,
23520 * NoLock is fine.
23521 */
23524
23525 if (OidIsValid(ownerId))
23526 {
23527 /* Do the partitions being merged have different owners? */
23528 if (ownerId != mergingPartition->rd_rel->relowner)
23529 ereport(ERROR,
23531 errmsg("partitions being merged have different owners"));
23532 }
23533 else
23534 ownerId = mergingPartition->rd_rel->relowner;
23535
23536 /* Store the next merging partition into the list. */
23539
23541 }
23542
23543 /* Look up the existing relation by the new partition name. */
23545
23546 /*
23547 * Check if this name is already taken. This helps us to detect the
23548 * situation when one of the merging partitions has the same name as the
23549 * new partition. Otherwise, this would fail later on anyway, but
23550 * catching this here allows us to emit a nicer error message.
23551 */
23553 {
23555 {
23556 /*
23557 * The new partition has the same name as one of the merging
23558 * partitions.
23559 */
23560 char tmpRelName[NAMEDATALEN];
23561
23562 /* Generate a temporary name. */
23563 sprintf(tmpRelName, "merge-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
23564
23565 /*
23566 * Rename the existing partition with a temporary name, leaving it
23567 * free for the new partition. We don't need to care about this
23568 * in the future because we're going to eventually drop the
23569 * existing partition anyway.
23570 */
23572
23573 /*
23574 * We must bump the command counter to make the new partition
23575 * tuple visible for rename.
23576 */
23578 }
23579 else
23580 {
23581 ereport(ERROR,
23583 errmsg("relation \"%s\" already exists", cmd->name->relname));
23584 }
23585 }
23586
23589
23590 /*
23591 * Collect extension dependencies from indexes on the merging partitions.
23592 * We must do this before detaching them, so we can restore the
23593 * dependencies on the new partition's indexes later.
23594 */
23596
23597 /* Detach all merging partitions. */
23599 {
23601
23603
23605
23607 }
23608
23609 /*
23610 * Perform a preliminary check to determine whether it's safe to drop all
23611 * merging partitions before we actually do so later. After merging rows
23612 * into the new partitions via MergePartitionsMoveRows, all old partitions
23613 * need to be dropped. However, since the drop behavior is DROP_RESTRICT
23614 * and the merge process (MergePartitionsMoveRows) can be time-consuming,
23615 * performing an early check on the drop eligibility of old partitions is
23616 * preferable.
23617 */
23619 {
23620 ObjectAddress object;
23621
23622 /* Get oid of the later to be dropped relation. */
23624 object.classId = RelationRelationId;
23625 object.objectSubId = 0;
23626
23628 }
23629
23630 /*
23631 * Create a table for the new partition, using the partitioned table as a
23632 * model.
23633 */
23634 Assert(OidIsValid(ownerId));
23635 newPartRel = createPartitionTable(wqueue, cmd->name, rel, ownerId);
23636
23637 /*
23638 * Switch to the table owner's userid, so that any index functions are run
23639 * as that user. Also, lockdown security-restricted operations and
23640 * arrange to make GUC variable changes local to this command.
23641 *
23642 * Need to do it after determining the namespace in the
23643 * createPartitionTable() call.
23644 */
23645 GetUserIdAndSecContext(&save_userid, &save_sec_context);
23646 SetUserIdAndSecContext(ownerId,
23647 save_sec_context | SECURITY_RESTRICTED_OPERATION);
23648 save_nestlevel = NewGUCNestLevel();
23650
23651 /* Copy data from merged partitions to the new partition. */
23653
23654 /* Drop the current partitions before attaching the new one. */
23656 {
23657 ObjectAddress object;
23658
23660 object.classId = RelationRelationId;
23661 object.objectSubId = 0;
23662
23663 performDeletion(&object, DROP_RESTRICT, 0);
23664 }
23665
23667
23668 /*
23669 * Attach a new partition to the partitioned table. wqueue = NULL:
23670 * verification for each cloned constraint is not needed.
23671 */
23673
23674 /*
23675 * Apply extension dependencies to the new partition's indexes. This
23676 * preserves any "DEPENDS ON EXTENSION" settings from the merged
23677 * partitions.
23678 */
23680
23682
23683 /* Keep the lock until commit. */
23685
23686 /* Roll back any GUC changes executed by index functions. */
23687 AtEOXact_GUC(false, save_nestlevel);
23688
23689 /* Restore the userid and security context. */
23690 SetUserIdAndSecContext(save_userid, save_sec_context);
23691}
void performDeletionCheck(const ObjectAddress *object, DropBehavior behavior, int flags)
Definition dependency.c:338
int MyProcPid
Definition globals.c:49
int NewGUCNestLevel(void)
Definition guc.c:2142
void RestrictSearchPath(void)
Definition guc.c:2153
void AtEOXact_GUC(bool isCommit, int nestLevel)
Definition guc.c:2169
#define SECURITY_RESTRICTED_OPERATION
Definition miscadmin.h:322
void GetUserIdAndSecContext(Oid *userid, int *sec_context)
Definition miscinit.c:613
void SetUserIdAndSecContext(Oid userid, int sec_context)
Definition miscinit.c:620
#define sprintf
Definition port.h:263
List * partlist
Relation table_openrv_extended(const RangeVar *relation, LOCKMODE lockmode, bool missing_ok)
Definition table.c:103
static void MergePartitionsMoveRows(List **wqueue, List *mergingPartitions, Relation newPartRel)
static void applyPartitionIndexExtDeps(Oid newPartOid, List *extDepState)
static Relation createPartitionTable(List **wqueue, RangeVar *newPartName, Relation parent_rel, Oid ownerId)
static void detachPartitionTable(Relation parent_rel, Relation child_rel, Oid defaultPartOid)
static List * collectPartitionIndexExtDeps(List *partitionOids)
static void freePartitionIndexExtDeps(List *extDepState)

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

Referenced by ATExecCmd().

◆ ATExecReplicaIdentity()

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

Definition at line 18956 of file tablecmds.c.

18957{
18958 Oid indexOid;
18959 Relation indexRel;
18960 int key;
18961
18962 if (stmt->identity_type == REPLICA_IDENTITY_DEFAULT)
18963 {
18964 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18965 return;
18966 }
18967 else if (stmt->identity_type == REPLICA_IDENTITY_FULL)
18968 {
18969 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18970 return;
18971 }
18972 else if (stmt->identity_type == REPLICA_IDENTITY_NOTHING)
18973 {
18974 relation_mark_replica_identity(rel, stmt->identity_type, InvalidOid, true);
18975 return;
18976 }
18977 else if (stmt->identity_type == REPLICA_IDENTITY_INDEX)
18978 {
18979 /* fallthrough */ ;
18980 }
18981 else
18982 elog(ERROR, "unexpected identity type %u", stmt->identity_type);
18983
18984 /* Check that the index exists */
18985 indexOid = get_relname_relid(stmt->name, rel->rd_rel->relnamespace);
18986 if (!OidIsValid(indexOid))
18987 ereport(ERROR,
18989 errmsg("index \"%s\" for table \"%s\" does not exist",
18990 stmt->name, RelationGetRelationName(rel))));
18991
18992 indexRel = index_open(indexOid, ShareLock);
18993
18994 /* Check that the index is on the relation we're altering. */
18995 if (indexRel->rd_index == NULL ||
18996 indexRel->rd_index->indrelid != RelationGetRelid(rel))
18997 ereport(ERROR,
18999 errmsg("\"%s\" is not an index for table \"%s\"",
19000 RelationGetRelationName(indexRel),
19002
19003 /*
19004 * The AM must support uniqueness, and the index must in fact be unique.
19005 * If we have a WITHOUT OVERLAPS constraint (identified by uniqueness +
19006 * exclusion), we can use that too.
19007 */
19008 if ((!indexRel->rd_indam->amcanunique ||
19009 !indexRel->rd_index->indisunique) &&
19010 !(indexRel->rd_index->indisunique && indexRel->rd_index->indisexclusion))
19011 ereport(ERROR,
19013 errmsg("cannot use non-unique index \"%s\" as replica identity",
19014 RelationGetRelationName(indexRel))));
19015 /* Deferred indexes are not guaranteed to be always unique. */
19016 if (!indexRel->rd_index->indimmediate)
19017 ereport(ERROR,
19019 errmsg("cannot use non-immediate index \"%s\" as replica identity",
19020 RelationGetRelationName(indexRel))));
19021 /* Expression indexes aren't supported. */
19022 if (RelationGetIndexExpressions(indexRel) != NIL)
19023 ereport(ERROR,
19025 errmsg("cannot use expression index \"%s\" as replica identity",
19026 RelationGetRelationName(indexRel))));
19027 /* Predicate indexes aren't supported. */
19028 if (RelationGetIndexPredicate(indexRel) != NIL)
19029 ereport(ERROR,
19031 errmsg("cannot use partial index \"%s\" as replica identity",
19032 RelationGetRelationName(indexRel))));
19033
19034 /* Check index for nullable columns. */
19035 for (key = 0; key < IndexRelationGetNumberOfKeyAttributes(indexRel); key++)
19036 {
19037 int16 attno = indexRel->rd_index->indkey.values[key];
19038 Form_pg_attribute attr;
19039
19040 /*
19041 * Reject any other system columns. (Going forward, we'll disallow
19042 * indexes containing such columns in the first place, but they might
19043 * exist in older branches.)
19044 */
19045 if (attno <= 0)
19046 ereport(ERROR,
19048 errmsg("index \"%s\" cannot be used as replica identity because column %d is a system column",
19049 RelationGetRelationName(indexRel), attno)));
19050
19051 attr = TupleDescAttr(rel->rd_att, attno - 1);
19052 if (!attr->attnotnull)
19053 ereport(ERROR,
19055 errmsg("index \"%s\" cannot be used as replica identity because column \"%s\" is nullable",
19056 RelationGetRelationName(indexRel),
19057 NameStr(attr->attname))));
19058 }
19059
19060 /* This index is suitable for use as a replica identity. Mark it. */
19061 relation_mark_replica_identity(rel, stmt->identity_type, indexOid, true);
19062
19063 index_close(indexRel, NoLock);
19064}
#define IndexRelationGetNumberOfKeyAttributes(relation)
Definition rel.h:535
List * RelationGetIndexPredicate(Relation relation)
Definition relcache.c:5220
List * RelationGetIndexExpressions(Relation relation)
Definition relcache.c:5107
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 17000 of file tablecmds.c.

17001{
17004 HeapTuple tuple;
17005 Form_pg_class rd_rel;
17006 Oid reloid = RelationGetRelid(rel);
17007
17008 /*
17009 * Shouldn't be called on relations having storage; these are processed in
17010 * phase 3.
17011 */
17012 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
17013
17014 /* Get a modifiable copy of the relation's pg_class row. */
17016
17018 if (!HeapTupleIsValid(tuple))
17019 elog(ERROR, "cache lookup failed for relation %u", reloid);
17020 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
17021
17022 /* Update the pg_class row. */
17023 oldAccessMethodId = rd_rel->relam;
17024 rd_rel->relam = newAccessMethodId;
17025
17026 /* Leave if no update required */
17027 if (rd_rel->relam == oldAccessMethodId)
17028 {
17029 heap_freetuple(tuple);
17031 return;
17032 }
17033
17034 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
17035
17036 /*
17037 * Update the dependency on the new access method. No dependency is added
17038 * if the new access method is InvalidOid (default case). Be very careful
17039 * that this has to compare the previous value stored in pg_class with the
17040 * new one.
17041 */
17042 if (!OidIsValid(oldAccessMethodId) && OidIsValid(rd_rel->relam))
17043 {
17045 referenced;
17046
17047 /*
17048 * New access method is defined and there was no dependency
17049 * previously, so record a new one.
17050 */
17054 }
17055 else if (OidIsValid(oldAccessMethodId) &&
17056 !OidIsValid(rd_rel->relam))
17057 {
17058 /*
17059 * There was an access method defined, and no new one, so just remove
17060 * the existing dependency.
17061 */
17065 }
17066 else
17067 {
17069 OidIsValid(rd_rel->relam));
17070
17071 /* Both are valid, so update the dependency */
17074 oldAccessMethodId, rd_rel->relam);
17075 }
17076
17077 /* make the relam and dependency changes visible */
17079
17081
17082 heap_freetuple(tuple);
17084}

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

19214{
19216 HeapTuple tuple;
19219 char *compression;
19220 char cmethod;
19221 ObjectAddress address;
19222
19223 compression = strVal(newValue);
19224
19226
19227 /* copy the cache entry so we can scribble on it below */
19229 if (!HeapTupleIsValid(tuple))
19230 ereport(ERROR,
19232 errmsg("column \"%s\" of relation \"%s\" does not exist",
19234
19235 /* prevent them from altering a system attribute */
19237 attnum = atttableform->attnum;
19238 if (attnum <= 0)
19239 ereport(ERROR,
19241 errmsg("cannot alter system column \"%s\"", column)));
19242
19243 /*
19244 * Check that column type is compressible, then get the attribute
19245 * compression method code
19246 */
19247 cmethod = GetAttributeCompression(atttableform->atttypid, compression);
19248
19249 /* update pg_attribute entry */
19250 atttableform->attcompression = cmethod;
19251 CatalogTupleUpdate(attrel, &tuple->t_self, tuple);
19252
19254 RelationGetRelid(rel),
19255 attnum);
19256
19257 /*
19258 * Apply the change to indexes as well (only for simple index columns,
19259 * matching behavior of index.c ConstructTupleDescriptor()).
19260 */
19262 false, 0,
19263 true, cmethod,
19264 lockmode);
19265
19266 heap_freetuple(tuple);
19267
19269
19270 /* make changes visible */
19272
19274 RelationGetRelid(rel), attnum);
19275 return address;
19276}
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:9214

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

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

References AddRelationNewConstraints(), AT_REWRITE_DEFAULT_VAL, AT_SetExpression, attnum, build_column_default(), CommandCounterIncrement(), deleteDependencyRecordsFor(), DROP_RESTRICT, elog, ereport, errcode(), errdetail(), errmsg, ERROR, expression_planner(), fb(), GetAttrDefaultOid(), GetRelationIncludedPublications(), GETSTRUCT(), HeapTupleIsValid, InvokeObjectPostAlterHook, lappend(), list_make1, newval, AlteredTableInfo::newvals, NIL, ObjectAddressSubSet, OidIsValid, palloc0_object, palloc_object, RelationClearMissing(), RelationGetRelationName, RelationGetRelid, ReleaseSysCache(), RememberAllDependentForRebuilding(), RemoveAttrDefault(), RemoveStatistics(), AlteredTableInfo::rewrite, SearchSysCacheAttName(), and AlteredTableInfo::verify_new_notnull.

Referenced by ATExecCmd().

◆ ATExecSetIdentity()

static ObjectAddress ATExecSetIdentity ( Relation  rel,
const char colName,
Node def,
LOCKMODE  lockmode,
bool  recurse,
bool  recursing 
)
static

Definition at line 8468 of file tablecmds.c.

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

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

9137{
9139 HeapTuple tuple,
9140 newtuple;
9143 Datum datum,
9144 newOptions;
9145 bool isnull;
9146 ObjectAddress address;
9150
9152
9154
9155 if (!HeapTupleIsValid(tuple))
9156 ereport(ERROR,
9158 errmsg("column \"%s\" of relation \"%s\" does not exist",
9161
9162 attnum = attrtuple->attnum;
9163 if (attnum <= 0)
9164 ereport(ERROR,
9166 errmsg("cannot alter system column \"%s\"",
9167 colName)));
9168
9169 /* Generate new proposed attoptions (text array) */
9171 &isnull);
9172 newOptions = transformRelOptions(isnull ? (Datum) 0 : datum,
9174 false, isReset);
9175 /* Validate new options */
9177
9178 /* Build new tuple. */
9179 memset(repl_null, false, sizeof(repl_null));
9180 memset(repl_repl, false, sizeof(repl_repl));
9181 if (newOptions != (Datum) 0)
9183 else
9188
9189 /* Update system catalog. */
9190 CatalogTupleUpdate(attrelation, &newtuple->t_self, newtuple);
9191
9193 RelationGetRelid(rel),
9194 attrtuple->attnum);
9196 RelationGetRelid(rel), attnum);
9197
9198 heap_freetuple(newtuple);
9199
9200 ReleaseSysCache(tuple);
9201
9203
9204 return address;
9205}
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 17120 of file tablecmds.c.

17122{
17123 Oid relid;
17125 HeapTuple tuple;
17126 HeapTuple newtuple;
17127 Datum datum;
17132 const char *const validnsps[] = HEAP_RELOPT_NAMESPACES;
17133
17135 return; /* nothing to do */
17136
17138
17139 /* Fetch heap tuple */
17140 relid = RelationGetRelid(rel);
17142 if (!HeapTupleIsValid(tuple))
17143 elog(ERROR, "cache lookup failed for relation %u", relid);
17144
17146 {
17147 /*
17148 * If we're supposed to replace the reloptions list, we just pretend
17149 * there were none before.
17150 */
17151 datum = (Datum) 0;
17152 }
17153 else
17154 {
17155 bool isnull;
17156
17157 /* Get the old reloptions */
17159 &isnull);
17160 if (isnull)
17161 datum = (Datum) 0;
17162 }
17163
17164 /* Generate new proposed reloptions (text array) */
17167
17168 /* Validate */
17169 switch (rel->rd_rel->relkind)
17170 {
17171 case RELKIND_RELATION:
17172 case RELKIND_MATVIEW:
17173 (void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
17174 break;
17177 break;
17178 case RELKIND_VIEW:
17180 break;
17181 case RELKIND_INDEX:
17184 break;
17185 case RELKIND_TOASTVALUE:
17186 /* fall through to error -- shouldn't ever get here */
17187 default:
17188 ereport(ERROR,
17190 errmsg("cannot set options for relation \"%s\"",
17192 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
17193 break;
17194 }
17195
17196 /* Special-case validation of view options */
17197 if (rel->rd_rel->relkind == RELKIND_VIEW)
17198 {
17201 ListCell *cell;
17202 bool check_option = false;
17203
17204 foreach(cell, view_options)
17205 {
17206 DefElem *defel = (DefElem *) lfirst(cell);
17207
17208 if (strcmp(defel->defname, "check_option") == 0)
17209 check_option = true;
17210 }
17211
17212 /*
17213 * If the check option is specified, look to see if the view is
17214 * actually auto-updatable or not.
17215 */
17216 if (check_option)
17217 {
17218 const char *view_updatable_error =
17220
17222 ereport(ERROR,
17224 errmsg("WITH CHECK OPTION is supported only on automatically updatable views"),
17225 errhint("%s", _(view_updatable_error))));
17226 }
17227 }
17228
17229 /*
17230 * All we need do here is update the pg_class row; the new options will be
17231 * propagated into relcaches during post-commit cache inval.
17232 */
17233 memset(repl_val, 0, sizeof(repl_val));
17234 memset(repl_null, false, sizeof(repl_null));
17235 memset(repl_repl, false, sizeof(repl_repl));
17236
17237 if (newOptions != (Datum) 0)
17239 else
17241
17243
17244 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17246
17247 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17249
17251
17252 heap_freetuple(newtuple);
17253
17254 ReleaseSysCache(tuple);
17255
17256 /* repeat the whole exercise for the toast table, if there's one */
17257 if (OidIsValid(rel->rd_rel->reltoastrelid))
17258 {
17260 Oid toastid = rel->rd_rel->reltoastrelid;
17261
17262 toastrel = table_open(toastid, lockmode);
17263
17264 /* Fetch heap tuple */
17266 if (!HeapTupleIsValid(tuple))
17267 elog(ERROR, "cache lookup failed for relation %u", toastid);
17268
17270 {
17271 /*
17272 * If we're supposed to replace the reloptions list, we just
17273 * pretend there were none before.
17274 */
17275 datum = (Datum) 0;
17276 }
17277 else
17278 {
17279 bool isnull;
17280
17281 /* Get the old reloptions */
17283 &isnull);
17284 if (isnull)
17285 datum = (Datum) 0;
17286 }
17287
17289 false, operation == AT_ResetRelOptions);
17290
17292
17293 memset(repl_val, 0, sizeof(repl_val));
17294 memset(repl_null, false, sizeof(repl_null));
17295 memset(repl_repl, false, sizeof(repl_repl));
17296
17297 if (newOptions != (Datum) 0)
17299 else
17301
17303
17304 newtuple = heap_modify_tuple(tuple, RelationGetDescr(pgclass),
17306
17307 CatalogTupleUpdate(pgclass, &newtuple->t_self, newtuple);
17308
17311 InvalidOid, true);
17312
17313 heap_freetuple(newtuple);
17314
17315 ReleaseSysCache(tuple);
17316
17318 }
17319
17321}
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 19070 of file tablecmds.c.

19071{
19073 Oid relid;
19074 HeapTuple tuple;
19075
19076 relid = RelationGetRelid(rel);
19077
19078 /* Pull the record for this relation and update it */
19080
19082
19083 if (!HeapTupleIsValid(tuple))
19084 elog(ERROR, "cache lookup failed for relation %u", relid);
19085
19087 CatalogTupleUpdate(pg_class, &tuple->t_self, tuple);
19088
19090 RelationGetRelid(rel), 0);
19091
19093 heap_freetuple(tuple);
19094}

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

8991{
8992 int newtarget = 0;
8993 bool newtarget_default;
8995 HeapTuple tuple,
8996 newtuple;
8999 ObjectAddress address;
9003
9004 /*
9005 * We allow referencing columns by numbers only for indexes, since table
9006 * column numbers could contain gaps if columns are later dropped.
9007 */
9008 if (rel->rd_rel->relkind != RELKIND_INDEX &&
9009 rel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX &&
9010 !colName)
9011 ereport(ERROR,
9013 errmsg("cannot refer to non-index column by number")));
9014
9015 /* -1 was used in previous versions for the default setting */
9016 if (newValue && intVal(newValue) != -1)
9017 {
9019 newtarget_default = false;
9020 }
9021 else
9022 newtarget_default = true;
9023
9024 if (!newtarget_default)
9025 {
9026 /*
9027 * Limit target to a sane range
9028 */
9029 if (newtarget < 0)
9030 {
9031 ereport(ERROR,
9033 errmsg("statistics target %d is too low",
9034 newtarget)));
9035 }
9037 {
9041 errmsg("lowering statistics target to %d",
9042 newtarget)));
9043 }
9044 }
9045
9047
9048 if (colName)
9049 {
9051
9052 if (!HeapTupleIsValid(tuple))
9053 ereport(ERROR,
9055 errmsg("column \"%s\" of relation \"%s\" does not exist",
9057 }
9058 else
9059 {
9061
9062 if (!HeapTupleIsValid(tuple))
9063 ereport(ERROR,
9065 errmsg("column number %d of relation \"%s\" does not exist",
9067 }
9068
9070
9071 attnum = attrtuple->attnum;
9072 if (attnum <= 0)
9073 ereport(ERROR,
9075 errmsg("cannot alter system column \"%s\"",
9076 colName)));
9077
9078 /*
9079 * Prevent this as long as the ANALYZE code skips virtual generated
9080 * columns.
9081 */
9082 if (attrtuple->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
9083 ereport(ERROR,
9085 errmsg("cannot alter statistics on virtual generated column \"%s\"",
9086 colName)));
9087
9088 if (rel->rd_rel->relkind == RELKIND_INDEX ||
9089 rel->rd_rel->relkind == RELKIND_PARTITIONED_INDEX)
9090 {
9091 if (attnum > rel->rd_index->indnkeyatts)
9092 ereport(ERROR,
9094 errmsg("cannot alter statistics on included column \"%s\" of index \"%s\"",
9095 NameStr(attrtuple->attname), RelationGetRelationName(rel))));
9096 else if (rel->rd_index->indkey.values[attnum - 1] != 0)
9097 ereport(ERROR,
9099 errmsg("cannot alter statistics on non-expression column \"%s\" of index \"%s\"",
9100 NameStr(attrtuple->attname), RelationGetRelationName(rel)),
9101 errhint("Alter statistics on table column instead.")));
9102 }
9103
9104 /* Build new tuple. */
9105 memset(repl_null, false, sizeof(repl_null));
9106 memset(repl_repl, false, sizeof(repl_repl));
9107 if (!newtarget_default)
9109 else
9114 CatalogTupleUpdate(attrelation, &tuple->t_self, newtuple);
9115
9117 RelationGetRelid(rel),
9118 attrtuple->attnum);
9120 RelationGetRelid(rel), attnum);
9121
9122 heap_freetuple(newtuple);
9123
9124 ReleaseSysCache(tuple);
9125
9127
9128 return address;
9129}
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 9277 of file tablecmds.c.

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

17329{
17330 Relation rel;
17335 ListCell *lc;
17336
17337 /*
17338 * Need lock here in case we are recursing to toast table or index
17339 */
17340 rel = relation_open(tableOid, lockmode);
17341
17342 /* Check first if relation can be moved to new tablespace */
17343 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
17344 {
17346 RelationGetRelid(rel), 0);
17347 relation_close(rel, NoLock);
17348 return;
17349 }
17350
17351 reltoastrelid = rel->rd_rel->reltoastrelid;
17352 /* Fetch the list of indexes on toast relation if necessary */
17354 {
17356
17358 relation_close(toastRel, lockmode);
17359 }
17360
17361 /*
17362 * Relfilenumbers are not unique in databases across tablespaces, so we
17363 * need to allocate a new one in the new tablespace.
17364 */
17365 newrelfilenumber = GetNewRelFileNumber(newTableSpace, NULL,
17366 rel->rd_rel->relpersistence);
17367
17368 /* Open old and new relation */
17369 newrlocator = rel->rd_locator;
17371 newrlocator.spcOid = newTableSpace;
17372
17373 /* hand off to AM to actually create new rel storage and copy the data */
17374 if (rel->rd_rel->relkind == RELKIND_INDEX)
17375 {
17377 }
17378 else
17379 {
17380 Assert(RELKIND_HAS_TABLE_AM(rel->rd_rel->relkind));
17382 }
17383
17384 /*
17385 * Update the pg_class row.
17386 *
17387 * NB: This wouldn't work if ATExecSetTableSpace() were allowed to be
17388 * executed on pg_class or its indexes (the above copy wouldn't contain
17389 * the updated pg_class entry), but that's forbidden with
17390 * CheckRelationTableSpaceMove().
17391 */
17392 SetRelationTableSpace(rel, newTableSpace, newrelfilenumber);
17393
17395
17397
17398 relation_close(rel, NoLock);
17399
17400 /* Make sure the reltablespace change is visible */
17402
17403 /* Move associated toast relation and/or indexes, too */
17405 ATExecSetTableSpace(reltoastrelid, newTableSpace, lockmode);
17406 foreach(lc, reltoastidxids)
17407 ATExecSetTableSpace(lfirst_oid(lc), newTableSpace, lockmode);
17408
17409 /* Clean up */
17411}
RelFileNumber GetNewRelFileNumber(Oid reltablespace, Relation pg_class, char relpersistence)
Definition catalog.c:557
void RelationAssumeNewRelfilelocator(Relation relation)
Definition relcache.c:3980
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:3757
void SetRelationTableSpace(Relation rel, Oid newTableSpaceId, RelFileNumber newRelFilenumber)
Definition tablecmds.c:3814
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 17421 of file tablecmds.c.

17422{
17423 /*
17424 * Shouldn't be called on relations having storage; these are processed in
17425 * phase 3.
17426 */
17427 Assert(!RELKIND_HAS_STORAGE(rel->rd_rel->relkind));
17428
17429 /* check if relation can be moved to its new tablespace */
17430 if (!CheckRelationTableSpaceMove(rel, newTableSpace))
17431 {
17433 RelationGetRelid(rel),
17434 0);
17435 return;
17436 }
17437
17438 /* Update can be done, so change reltablespace */
17439 SetRelationTableSpace(rel, newTableSpace, InvalidOid);
17440
17442
17443 /* Make sure the reltablespace change is visible */
17445}

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

23969{
23973 *listptr2;
23974 bool isSameName = false;
23975 char tmpRelName[NAMEDATALEN];
23976 List *newPartRels = NIL;
23977 List *extDepState = NIL;
23978 ObjectAddress object;
23980 Oid save_userid;
23981 int save_sec_context;
23982 int save_nestlevel;
23984
23986
23987 /*
23988 * Partition is already locked in the transformPartitionCmdForSplit
23989 * function.
23990 */
23992
23994
23995 /* Check descriptions of new partitions. */
23997 {
23999
24000 /* Look up the existing relation by the new partition name. */
24002
24003 /*
24004 * This would fail later on anyway if the relation already exists. But
24005 * by catching it here, we can emit a nicer error message.
24006 */
24008 /* One new partition can have the same name as a split partition. */
24009 isSameName = true;
24010 else if (OidIsValid(existingRelid))
24011 ereport(ERROR,
24013 errmsg("relation \"%s\" already exists", sps->name->relname));
24014 }
24015
24016 /*
24017 * Collect extension dependencies from indexes on the split partition. We
24018 * must do this before detaching it, so we can restore the dependencies on
24019 * the new partitions' indexes later.
24020 */
24022
24025
24026 /* Detach the split partition. */
24028
24029 /*
24030 * Perform a preliminary check to determine whether it's safe to drop the
24031 * split partition before we actually do so later. After merging rows into
24032 * the new partitions via SplitPartitionMoveRows, all old partitions need
24033 * to be dropped. However, since the drop behavior is DROP_RESTRICT and
24034 * the merge process (SplitPartitionMoveRows) can be time-consuming,
24035 * performing an early check on the drop eligibility of old partitions is
24036 * preferable.
24037 */
24038 object.objectId = splitRelOid;
24039 object.classId = RelationRelationId;
24040 object.objectSubId = 0;
24042
24043 /*
24044 * If a new partition has the same name as the split partition, then we
24045 * should rename the split partition to reuse its name.
24046 */
24047 if (isSameName)
24048 {
24049 /*
24050 * We must bump the command counter to make the split partition tuple
24051 * visible for renaming.
24052 */
24054 /* Rename partition. */
24055 sprintf(tmpRelName, "split-%u-%X-tmp", RelationGetRelid(rel), MyProcPid);
24057
24058 /*
24059 * We must bump the command counter to make the split partition tuple
24060 * visible after renaming.
24061 */
24063 }
24064
24065 /* Create new partitions (like a split partition), without indexes. */
24067 {
24069
24071 splitRel->rd_rel->relowner);
24073 }
24074
24075 /*
24076 * Switch to the table owner's userid, so that any index functions are run
24077 * as that user. Also, lockdown security-restricted operations and
24078 * arrange to make GUC variable changes local to this command.
24079 *
24080 * Need to do it after determining the namespace in the
24081 * createPartitionTable() call.
24082 */
24083 GetUserIdAndSecContext(&save_userid, &save_sec_context);
24084 SetUserIdAndSecContext(splitRel->rd_rel->relowner,
24085 save_sec_context | SECURITY_RESTRICTED_OPERATION);
24086 save_nestlevel = NewGUCNestLevel();
24088
24089 /* Copy data from the split partition to the new partitions. */
24091 /* Keep the lock until commit. */
24093
24094 /* Attach new partitions to the partitioned table. */
24096 {
24099
24100 /*
24101 * wqueue = NULL: verification for each cloned constraint is not
24102 * needed.
24103 */
24104 attachPartitionTable(NULL, rel, newPartRel, sps->bound);
24105
24106 /*
24107 * Apply extension dependencies to the new partition's indexes. This
24108 * preserves any "DEPENDS ON EXTENSION" settings from the split
24109 * partition.
24110 */
24112
24113 /* Keep the lock until commit. */
24115 }
24116
24118
24119 /* Drop the split partition. */
24120 object.classId = RelationRelationId;
24121 object.objectId = splitRelOid;
24122 object.objectSubId = 0;
24123 /* Probably DROP_CASCADE is not needed. */
24124 performDeletion(&object, DROP_RESTRICT, 0);
24125
24126 /* Roll back any GUC changes executed by index functions. */
24127 AtEOXact_GUC(false, save_nestlevel);
24128
24129 /* Restore the userid and security context. */
24130 SetUserIdAndSecContext(save_userid, save_sec_context);
24131}
struct RelationData * Relation
Definition genam.h:30
#define forboth(cell1, list1, cell2, list2)
Definition pg_list.h:550
static void SplitPartitionMoveRows(List **wqueue, Relation rel, Relation splitRel, List *partlist, List *newPartRels)

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

Referenced by ATExecCmd().

◆ ATExecValidateConstraint()

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

Definition at line 13379 of file tablecmds.c.

13381{
13383 SysScanDesc scan;
13384 ScanKeyData skey[3];
13385 HeapTuple tuple;
13387 ObjectAddress address;
13388
13390
13391 /*
13392 * Find and check the target constraint
13393 */
13394 ScanKeyInit(&skey[0],
13398 ScanKeyInit(&skey[1],
13402 ScanKeyInit(&skey[2],
13407 true, NULL, 3, skey);
13408
13409 /* There can be at most one matching row */
13410 if (!HeapTupleIsValid(tuple = systable_getnext(scan)))
13411 ereport(ERROR,
13413 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
13415
13416 con = (Form_pg_constraint) GETSTRUCT(tuple);
13417 if (con->contype != CONSTRAINT_FOREIGN &&
13418 con->contype != CONSTRAINT_CHECK &&
13419 con->contype != CONSTRAINT_NOTNULL)
13420 ereport(ERROR,
13422 errmsg("cannot validate constraint \"%s\" of relation \"%s\"",
13424 errdetail("This operation is not supported for this type of constraint."));
13425
13426 if (!con->conenforced)
13427 ereport(ERROR,
13429 errmsg("cannot validate NOT ENFORCED constraint")));
13430
13431 if (!con->convalidated)
13432 {
13433 if (con->contype == CONSTRAINT_FOREIGN)
13434 {
13435 QueueFKConstraintValidation(wqueue, conrel, rel, con->confrelid,
13436 tuple, lockmode);
13437 }
13438 else if (con->contype == CONSTRAINT_CHECK)
13439 {
13441 tuple, recurse, recursing, lockmode);
13442 }
13443 else if (con->contype == CONSTRAINT_NOTNULL)
13444 {
13446 tuple, recurse, recursing, lockmode);
13447 }
13448
13449 ObjectAddressSet(address, ConstraintRelationId, con->oid);
13450 }
13451 else
13452 address = InvalidObjectAddress; /* already validated */
13453
13454 systable_endscan(scan);
13455
13457
13458 return address;
13459}
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 6655 of file tablecmds.c.

6656{
6657 Oid relid = RelationGetRelid(rel);
6658 AlteredTableInfo *tab;
6659 ListCell *ltab;
6660
6661 foreach(ltab, *wqueue)
6662 {
6663 tab = (AlteredTableInfo *) lfirst(ltab);
6664 if (tab->relid == relid)
6665 return tab;
6666 }
6667
6668 /*
6669 * Not there, so add it. Note that we make a copy of the relation's
6670 * existing descriptor before anything interesting can happen to it.
6671 */
6673 tab->relid = relid;
6674 tab->rel = NULL; /* set later */
6675 tab->relkind = rel->rd_rel->relkind;
6678 tab->chgAccessMethod = false;
6681 tab->chgPersistence = false;
6682
6683 *wqueue = lappend(*wqueue, tab);
6684
6685 return tab;
6686}
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 5802 of file tablecmds.c.

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

15911{
15912 ObjectAddress obj;
15913 ObjectAddresses *objects;
15916
15917 /*
15918 * Collect all the constraints and indexes to drop so we can process them
15919 * in a single call. That way we don't have to worry about dependencies
15920 * among them.
15921 */
15922 objects = new_object_addresses();
15923
15924 /*
15925 * Re-parse the index and constraint definitions, and attach them to the
15926 * appropriate work queue entries. We do this before dropping because in
15927 * the case of a constraint on another table, we might not yet have
15928 * exclusive lock on the table the constraint is attached to, and we need
15929 * to get that before reparsing/dropping. (That's possible at least for
15930 * FOREIGN KEY, CHECK, and EXCLUSION constraints; in non-FK cases it
15931 * requires a dependency on the target table's composite type in the other
15932 * table's constraint expressions.)
15933 *
15934 * We can't rely on the output of deparsing to tell us which relation to
15935 * operate on, because concurrent activity might have made the name
15936 * resolve differently. Instead, we've got to use the OID of the
15937 * constraint or index we're processing to figure out which relation to
15938 * operate on.
15939 */
15942 {
15944 HeapTuple tup;
15946 Oid relid;
15947 Oid confrelid;
15948 bool conislocal;
15949
15951 if (!HeapTupleIsValid(tup)) /* should not happen */
15952 elog(ERROR, "cache lookup failed for constraint %u", oldId);
15954 if (OidIsValid(con->conrelid))
15955 relid = con->conrelid;
15956 else
15957 {
15958 /* must be a domain constraint */
15959 relid = get_typ_typrelid(getBaseType(con->contypid));
15960 if (!OidIsValid(relid))
15961 elog(ERROR, "could not identify relation associated with constraint %u", oldId);
15962 }
15963 confrelid = con->confrelid;
15964 conislocal = con->conislocal;
15966
15968 add_exact_object_address(&obj, objects);
15969
15970 /*
15971 * If the constraint is inherited (only), we don't want to inject a
15972 * new definition here; it'll get recreated when
15973 * ATAddCheckNNConstraint recurses from adding the parent table's
15974 * constraint. But we had to carry the info this far so that we can
15975 * drop the constraint below.
15976 */
15977 if (!conislocal)
15978 continue;
15979
15980 /*
15981 * When rebuilding another table's constraint that references the
15982 * table we're modifying, we might not yet have any lock on the other
15983 * table, so get one now. We'll need AccessExclusiveLock for the DROP
15984 * CONSTRAINT step, so there's no value in asking for anything weaker.
15985 */
15986 if (relid != tab->relid)
15988
15989 ATPostAlterTypeParse(oldId, relid, confrelid,
15990 (char *) lfirst(def_item),
15991 wqueue, lockmode, tab->rewrite);
15992 }
15995 {
15997 Oid relid;
15998
15999 relid = IndexGetRelation(oldId, false);
16000
16001 /*
16002 * As above, make sure we have lock on the index's table if it's not
16003 * the same table.
16004 */
16005 if (relid != tab->relid)
16007
16009 (char *) lfirst(def_item),
16010 wqueue, lockmode, tab->rewrite);
16011
16013 add_exact_object_address(&obj, objects);
16014 }
16015
16016 /* add dependencies for new statistics */
16019 {
16021 Oid relid;
16022
16023 relid = StatisticsGetRelation(oldId, false);
16024
16025 /*
16026 * As above, make sure we have lock on the statistics object's table
16027 * if it's not the same table. However, we take
16028 * ShareUpdateExclusiveLock here, aligning with the lock level used in
16029 * CreateStatistics and RemoveStatisticsById.
16030 *
16031 * CAUTION: this should be done after all cases that grab
16032 * AccessExclusiveLock, else we risk causing deadlock due to needing
16033 * to promote our table lock.
16034 */
16035 if (relid != tab->relid)
16037
16039 (char *) lfirst(def_item),
16040 wqueue, lockmode, tab->rewrite);
16041
16043 add_exact_object_address(&obj, objects);
16044 }
16045
16046 /*
16047 * Queue up command to restore replica identity index marking
16048 */
16049 if (tab->replicaIdentityIndex)
16050 {
16053
16054 subcmd->identity_type = REPLICA_IDENTITY_INDEX;
16055 subcmd->name = tab->replicaIdentityIndex;
16057 cmd->def = (Node *) subcmd;
16058
16059 /* do it after indexes and constraints */
16062 }
16063
16064 /*
16065 * Queue up command to restore marking of index used for cluster.
16066 */
16067 if (tab->clusterOnIndex)
16068 {
16070
16071 cmd->subtype = AT_ClusterOn;
16072 cmd->name = tab->clusterOnIndex;
16073
16074 /* do it after indexes and constraints */
16077 }
16078
16079 /*
16080 * It should be okay to use DROP_RESTRICT here, since nothing else should
16081 * be depending on these objects.
16082 */
16084
16085 free_object_addresses(objects);
16086
16087 /*
16088 * The objects will get recreated during subsequent passes over the work
16089 * queue.
16090 */
16091}
Oid IndexGetRelation(Oid indexId, bool missing_ok)
Definition index.c:3604
Oid get_typ_typrelid(Oid typid)
Definition lsyscache.c:3039
Oid StatisticsGetRelation(Oid statId, bool missing_ok)
Definition statscmds.c:954
List * changedConstraintDefs
Definition tablecmds.c:207
List * changedStatisticsDefs
Definition tablecmds.c:213
char * clusterOnIndex
Definition tablecmds.c:211
char * replicaIdentityIndex
Definition tablecmds.c:210
List * changedStatisticsOids
Definition tablecmds.c:212
List * changedIndexDefs
Definition tablecmds.c:209
List * changedIndexOids
Definition tablecmds.c:208
List * changedConstraintOids
Definition tablecmds.c:206
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, LOCKMODE lockmode, bool rewrite)

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

Referenced by ATRewriteCatalogs().

◆ ATPostAlterTypeParse()

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

Definition at line 16102 of file tablecmds.c.

16104{
16108 Relation rel;
16109
16110 /*
16111 * We expect that we will get only ALTER TABLE and CREATE INDEX
16112 * statements. Hence, there is no need to pass them through
16113 * parse_analyze_*() or the rewriter, but instead we need to pass them
16114 * through parse_utilcmd.c to make them ready for execution.
16115 */
16119 {
16121 Node *stmt = rs->stmt;
16122
16123 if (IsA(stmt, IndexStmt))
16126 (IndexStmt *) stmt,
16127 cmd));
16128 else if (IsA(stmt, AlterTableStmt))
16129 {
16131 List *afterStmts;
16132
16134 (AlterTableStmt *) stmt,
16135 cmd,
16136 &beforeStmts,
16137 &afterStmts);
16141 }
16142 else if (IsA(stmt, CreateStatsStmt))
16146 cmd));
16147 else
16149 }
16150
16151 /* Caller should already have acquired whatever lock we need. */
16153
16154 /*
16155 * Attach each generated command to the proper place in the work queue.
16156 * Note this could result in creation of entirely new work-queue entries.
16157 *
16158 * Also note that we have to tweak the command subtypes, because it turns
16159 * out that re-creation of indexes and constraints has to act a bit
16160 * differently from initial creation.
16161 */
16162 foreach(list_item, querytree_list)
16163 {
16164 Node *stm = (Node *) lfirst(list_item);
16165 AlteredTableInfo *tab;
16166
16167 tab = ATGetQueueEntry(wqueue, rel);
16168
16169 if (IsA(stm, IndexStmt))
16170 {
16171 IndexStmt *stmt = (IndexStmt *) stm;
16173
16174 if (!rewrite)
16176 stmt->reset_default_tblspc = true;
16177 /* keep the index's comment */
16178 stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
16179
16181 newcmd->subtype = AT_ReAddIndex;
16182 newcmd->def = (Node *) stmt;
16185 }
16186 else if (IsA(stm, AlterTableStmt))
16187 {
16189 ListCell *lcmd;
16190
16191 foreach(lcmd, stmt->cmds)
16192 {
16194
16195 if (cmd->subtype == AT_AddIndex)
16196 {
16198 Oid indoid;
16199
16200 indstmt = castNode(IndexStmt, cmd->def);
16201 indoid = get_constraint_index(oldId);
16202
16203 if (!rewrite)
16204 TryReuseIndex(indoid, indstmt);
16205 /* keep any comment on the index */
16206 indstmt->idxcomment = GetComment(indoid,
16208 indstmt->reset_default_tblspc = true;
16209
16210 cmd->subtype = AT_ReAddIndex;
16212 lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
16213
16214 /* recreate any comment on the constraint */
16217 oldId,
16218 rel,
16219 NIL,
16220 indstmt->idxname);
16221 }
16222 else if (cmd->subtype == AT_AddConstraint)
16223 {
16224 Constraint *con = castNode(Constraint, cmd->def);
16225
16227 /* rewriting neither side of a FK */
16228 if (con->contype == CONSTR_FOREIGN &&
16229 !rewrite && tab->rewrite == 0)
16231 con->reset_default_tblspc = true;
16235
16236 /*
16237 * Recreate any comment on the constraint. If we have
16238 * recreated a primary key, then transformTableConstraint
16239 * has added an unnamed not-null constraint here; skip
16240 * this in that case.
16241 */
16242 if (con->conname)
16245 oldId,
16246 rel,
16247 NIL,
16248 con->conname);
16249 else
16250 Assert(con->contype == CONSTR_NOTNULL);
16251 }
16252 else
16253 elog(ERROR, "unexpected statement subtype: %d",
16254 (int) cmd->subtype);
16255 }
16256 }
16257 else if (IsA(stm, AlterDomainStmt))
16258 {
16260
16261 if (stmt->subtype == AD_AddConstraint)
16262 {
16263 Constraint *con = castNode(Constraint, stmt->def);
16265
16267 cmd->def = (Node *) stmt;
16270
16271 /* recreate any comment on the constraint */
16274 oldId,
16275 NULL,
16276 stmt->typeName,
16277 con->conname);
16278 }
16279 else
16280 elog(ERROR, "unexpected statement subtype: %d",
16281 (int) stmt->subtype);
16282 }
16283 else if (IsA(stm, CreateStatsStmt))
16284 {
16287
16288 /* keep the statistics object's comment */
16289 stmt->stxcomment = GetComment(oldId, StatisticExtRelationId, 0);
16290
16292 newcmd->subtype = AT_ReAddStatistics;
16293 newcmd->def = (Node *) stmt;
16294 tab->subcmds[AT_PASS_MISC] =
16296 }
16297 else
16298 elog(ERROR, "unexpected statement type: %d",
16299 (int) nodeTag(stm));
16300 }
16301
16302 relation_close(rel, NoLock);
16303}
List * raw_parser(const char *str, RawParseMode mode)
Definition parser.c:42
char * GetComment(Oid oid, Oid classoid, int32 subid)
Definition comment.c:420
Oid get_constraint_index(Oid conoid)
Definition lsyscache.c:1331
#define nodeTag(nodeptr)
Definition nodes.h: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 7290 of file tablecmds.c.

7293{
7294 if (rel->rd_rel->reloftype && !recursing)
7295 ereport(ERROR,
7297 errmsg("cannot add column to typed table")));
7298
7299 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
7300 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
7301
7302 if (recurse && !is_view)
7303 cmd->recurse = true;
7304}
static void ATTypedTableRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:6988

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

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

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

14852{
14853 char *colName = cmd->name;
14854 ColumnDef *def = (ColumnDef *) cmd->def;
14855 TypeName *typeName = def->typeName;
14856 Node *transform = def->cooked_default;
14857 HeapTuple tuple;
14860 Oid targettype;
14861 int32 targettypmod;
14864 ParseState *pstate = make_parsestate(NULL);
14866 bool is_expr;
14867
14868 pstate->p_sourcetext = context->queryString;
14869
14870 if (rel->rd_rel->reloftype && !recursing)
14871 ereport(ERROR,
14873 errmsg("cannot alter column type of typed table"),
14874 parser_errposition(pstate, def->location)));
14875
14876 /* lookup the attribute so we can check inheritance status */
14878 if (!HeapTupleIsValid(tuple))
14879 ereport(ERROR,
14881 errmsg("column \"%s\" of relation \"%s\" does not exist",
14883 parser_errposition(pstate, def->location)));
14885 attnum = attTup->attnum;
14886
14887 /* Can't alter a system attribute */
14888 if (attnum <= 0)
14889 ereport(ERROR,
14891 errmsg("cannot alter system column \"%s\"", colName),
14892 parser_errposition(pstate, def->location)));
14893
14894 /*
14895 * Cannot specify USING when altering type of a generated column, because
14896 * that would violate the generation expression.
14897 */
14898 if (attTup->attgenerated && def->cooked_default)
14899 ereport(ERROR,
14901 errmsg("cannot specify USING when altering type of generated column"),
14902 errdetail("Column \"%s\" is a generated column.", colName),
14903 parser_errposition(pstate, def->location)));
14904
14905 /*
14906 * Don't alter inherited columns. At outer level, there had better not be
14907 * any inherited definition; when recursing, we assume this was checked at
14908 * the parent level (see below).
14909 */
14910 if (attTup->attinhcount > 0 && !recursing)
14911 ereport(ERROR,
14913 errmsg("cannot alter inherited column \"%s\"", colName),
14914 parser_errposition(pstate, def->location)));
14915
14916 /* Don't alter columns used in the partition key */
14917 if (has_partition_attrs(rel,
14919 &is_expr))
14920 ereport(ERROR,
14922 errmsg("cannot alter column \"%s\" because it is part of the partition key of relation \"%s\"",
14924 parser_errposition(pstate, def->location)));
14925
14926 /* Look up the target type */
14927 typenameTypeIdAndMod(pstate, typeName, &targettype, &targettypmod);
14928
14930 if (aclresult != ACLCHECK_OK)
14931 aclcheck_error_type(aclresult, targettype);
14932
14933 /* And the collation */
14934 targetcollid = GetColumnDefCollation(pstate, def, targettype);
14935
14936 /* make sure datatype is legal for a column */
14938 list_make1_oid(rel->rd_rel->reltype),
14939 (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL ? CHKATYPE_IS_VIRTUAL : 0));
14940
14941 if (attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
14942 {
14943 /* do nothing */
14944 }
14945 else if (tab->relkind == RELKIND_RELATION ||
14947 {
14948 /*
14949 * Set up an expression to transform the old data value to the new
14950 * type. If a USING option was given, use the expression as
14951 * transformed by transformAlterTableStmt, else just take the old
14952 * value and try to coerce it. We do this first so that type
14953 * incompatibility can be detected before we waste effort, and because
14954 * we need the expression to be parsed against the original table row
14955 * type.
14956 */
14957 if (!transform)
14958 {
14959 transform = (Node *) makeVar(1, attnum,
14960 attTup->atttypid, attTup->atttypmod,
14961 attTup->attcollation,
14962 0);
14963 }
14964
14965 transform = coerce_to_target_type(pstate,
14966 transform, exprType(transform),
14967 targettype, targettypmod,
14970 -1);
14971 if (transform == NULL)
14972 {
14973 /* error text depends on whether USING was specified or not */
14974 if (def->cooked_default != NULL)
14975 ereport(ERROR,
14977 errmsg("result of USING clause for column \"%s\""
14978 " cannot be cast automatically to type %s",
14979 colName, format_type_be(targettype)),
14980 errhint("You might need to add an explicit cast.")));
14981 else
14982 ereport(ERROR,
14984 errmsg("column \"%s\" cannot be cast automatically to type %s",
14985 colName, format_type_be(targettype)),
14986 !attTup->attgenerated ?
14987 /* translator: USING is SQL, don't translate it */
14988 errhint("You might need to specify \"USING %s::%s\".",
14990 format_type_with_typemod(targettype,
14991 targettypmod)) : 0));
14992 }
14993
14994 /* Fix collations after all else */
14995 assign_expr_collations(pstate, transform);
14996
14997 /* Expand virtual generated columns in the expr. */
14998 transform = expand_generated_columns_in_expr(transform, rel, 1);
14999
15000 /* Plan the expr now so we can accurately assess the need to rewrite. */
15001 transform = (Node *) expression_planner((Expr *) transform);
15002
15003 /*
15004 * Add a work queue item to make ATRewriteTable update the column
15005 * contents.
15006 */
15008 newval->attnum = attnum;
15009 newval->expr = (Expr *) transform;
15010 newval->is_generated = false;
15011
15012 tab->newvals = lappend(tab->newvals, newval);
15013 if (ATColumnChangeRequiresRewrite(transform, attnum))
15015 }
15016 else if (transform)
15017 ereport(ERROR,
15019 errmsg("\"%s\" is not a table",
15021
15022 if (!RELKIND_HAS_STORAGE(tab->relkind) || attTup->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
15023 {
15024 /*
15025 * For relations or columns without storage, do this check now.
15026 * Regular tables will check it later when the table is being
15027 * rewritten.
15028 */
15029 find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
15030 }
15031
15032 ReleaseSysCache(tuple);
15033
15034 /*
15035 * Recurse manually by queueing a new command for each child, if
15036 * necessary. We cannot apply ATSimpleRecursion here because we need to
15037 * remap attribute numbers in the USING expression, if any.
15038 *
15039 * If we are told not to recurse, there had better not be any child
15040 * tables; else the alter would put them out of step.
15041 */
15042 if (recurse)
15043 {
15044 Oid relid = RelationGetRelid(rel);
15047 ListCell *lo,
15048 *li;
15049
15050 child_oids = find_all_inheritors(relid, lockmode,
15052
15053 /*
15054 * find_all_inheritors does the recursive search of the inheritance
15055 * hierarchy, so all we have to do is process all of the relids in the
15056 * list that it returns.
15057 */
15059 {
15061 int numparents = lfirst_int(li);
15065
15066 if (childrelid == relid)
15067 continue;
15068
15069 /* find_all_inheritors already got lock */
15072
15073 /*
15074 * Verify that the child doesn't have any inherited definitions of
15075 * this column that came from outside this inheritance hierarchy.
15076 * (renameatt makes a similar test, though in a different way
15077 * because of its different recursion mechanism.)
15078 */
15080 colName);
15082 ereport(ERROR,
15084 errmsg("column \"%s\" of relation \"%s\" does not exist",
15087
15088 if (childattTup->attinhcount > numparents)
15089 ereport(ERROR,
15091 errmsg("cannot alter inherited column \"%s\" of relation \"%s\"",
15093
15095
15096 /*
15097 * Remap the attribute numbers. If no USING expression was
15098 * specified, there is no need for this step.
15099 */
15100 if (def->cooked_default)
15101 {
15102 AttrMap *attmap;
15103 bool found_whole_row;
15104
15105 /* create a copy to scribble on */
15106 cmd = copyObject(cmd);
15107
15109 RelationGetDescr(rel),
15110 false);
15111 ((ColumnDef *) cmd->def)->cooked_default =
15112 map_variable_attnos(def->cooked_default,
15113 1, 0,
15114 attmap,
15115 InvalidOid, &found_whole_row);
15116 if (found_whole_row)
15117 ereport(ERROR,
15119 errmsg("cannot convert whole-row table reference"),
15120 errdetail("USING expression contains a whole-row table reference.")));
15121 pfree(attmap);
15122 }
15123 ATPrepCmd(wqueue, childrel, cmd, false, true, lockmode, context);
15125 }
15126 }
15127 else if (!recursing &&
15129 ereport(ERROR,
15131 errmsg("type of inherited column \"%s\" must be changed in child tables too",
15132 colName)));
15133
15134 if (tab->relkind == RELKIND_COMPOSITE_TYPE)
15135 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
15136}
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:7033

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

17713{
17714 if (child_rel->rd_rel->reloftype)
17715 ereport(ERROR,
17717 errmsg("cannot change inheritance of typed table")));
17718
17719 if (child_rel->rd_rel->relispartition)
17720 ereport(ERROR,
17722 errmsg("cannot change inheritance of a partition")));
17723}

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

Referenced by ATPrepCmd().

◆ ATPrepChangePersistence()

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

Definition at line 19287 of file tablecmds.c.

19288{
19290 HeapTuple tuple;
19291 SysScanDesc scan;
19292 ScanKeyData skey[1];
19293
19294 /*
19295 * Disallow changing status for a temp table. Also verify whether we can
19296 * get away with doing nothing; in such cases we don't need to run the
19297 * checks below, either.
19298 */
19299 switch (rel->rd_rel->relpersistence)
19300 {
19302 ereport(ERROR,
19304 errmsg("cannot change logged status of table \"%s\" because it is temporary",
19306 errtable(rel)));
19307 break;
19309 if (toLogged)
19310 /* nothing to do */
19311 return;
19312 break;
19314 if (!toLogged)
19315 /* nothing to do */
19316 return;
19317 break;
19318 }
19319
19320 /*
19321 * Check that the table is not part of any publication when changing to
19322 * UNLOGGED, as UNLOGGED tables can't be published.
19323 */
19324 if (!toLogged &&
19326 ereport(ERROR,
19328 errmsg("cannot change table \"%s\" to unlogged because it is part of a publication",
19330 errdetail("Unlogged relations cannot be replicated.")));
19331
19332 /*
19333 * Check existing foreign key constraints to preserve the invariant that
19334 * permanent tables cannot reference unlogged ones. Self-referencing
19335 * foreign keys can safely be ignored.
19336 */
19338
19339 /*
19340 * Scan conrelid if changing to permanent, else confrelid. This also
19341 * determines whether a useful index exists.
19342 */
19343 ScanKeyInit(&skey[0],
19350 true, NULL, 1, skey);
19351
19352 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
19353 {
19355
19356 if (con->contype == CONSTRAINT_FOREIGN)
19357 {
19359 Relation foreignrel;
19360
19361 /* the opposite end of what we used as scankey */
19362 foreignrelid = toLogged ? con->confrelid : con->conrelid;
19363
19364 /* ignore if self-referencing */
19365 if (RelationGetRelid(rel) == foreignrelid)
19366 continue;
19367
19369
19370 if (toLogged)
19371 {
19372 if (!RelationIsPermanent(foreignrel))
19373 ereport(ERROR,
19375 errmsg("could not change table \"%s\" to logged because it references unlogged table \"%s\"",
19377 RelationGetRelationName(foreignrel)),
19378 errtableconstraint(rel, NameStr(con->conname))));
19379 }
19380 else
19381 {
19382 if (RelationIsPermanent(foreignrel))
19383 ereport(ERROR,
19385 errmsg("could not change table \"%s\" to unlogged because it references logged table \"%s\"",
19387 RelationGetRelationName(foreignrel)),
19388 errtableconstraint(rel, NameStr(con->conname))));
19389 }
19390
19391 relation_close(foreignrel, AccessShareLock);
19392 }
19393 }
19394
19395 systable_endscan(scan);
19396
19398
19399 /* force rewrite if necessary; see comment in ATRewriteTables */
19401 if (toLogged)
19403 else
19405 tab->chgPersistence = true;
19406}
#define AT_REWRITE_ALTER_PERSISTENCE
return true
Definition isn.c:130
int errtableconstraint(Relation rel, const char *conname)
Definition relcache.c:6136
int errtable(Relation rel)
Definition relcache.c:6082

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

4977{
4978 AlteredTableInfo *tab;
4980
4981 /* Find or create work queue entry for this table */
4982 tab = ATGetQueueEntry(wqueue, rel);
4983
4984 /*
4985 * Disallow any ALTER TABLE other than ALTER TABLE DETACH FINALIZE on
4986 * partitions that are pending detach.
4987 */
4988 if (rel->rd_rel->relispartition &&
4991 ereport(ERROR,
4993 errmsg("cannot alter partition \"%s\" with an incomplete detach",
4995 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
4996
4997 /*
4998 * Copy the original subcommand for each table, so we can scribble on it.
4999 * This avoids conflicts when different child tables need to make
5000 * different parse transformations (for example, the same column may have
5001 * different column numbers in different children).
5002 */
5003 cmd = copyObject(cmd);
5004
5005 /*
5006 * Do permissions and relkind checking, recursion to child tables if
5007 * needed, and any additional phase-1 processing needed. (But beware of
5008 * adding any processing that looks at table details that another
5009 * subcommand could change. In some cases we reject multiple subcommands
5010 * that could try to change the same state in contrary ways.)
5011 */
5012 switch (cmd->subtype)
5013 {
5014 case AT_AddColumn: /* ADD COLUMN */
5015 ATSimplePermissions(cmd->subtype, rel,
5018 ATPrepAddColumn(wqueue, rel, recurse, recursing, false, cmd,
5019 lockmode, context);
5020 /* Recursion occurs during execution phase */
5022 break;
5023 case AT_AddColumnToView: /* add column via CREATE OR REPLACE VIEW */
5025 ATPrepAddColumn(wqueue, rel, recurse, recursing, true, cmd,
5026 lockmode, context);
5027 /* Recursion occurs during execution phase */
5029 break;
5030 case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
5031
5032 /*
5033 * We allow defaults on views so that INSERT into a view can have
5034 * default-ish behavior. This works because the rewriter
5035 * substitutes default values into INSERTs before it expands
5036 * rules.
5037 */
5038 ATSimplePermissions(cmd->subtype, rel,
5041 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5042 /* No command-specific prep needed */
5044 break;
5045 case AT_CookedColumnDefault: /* add a pre-cooked default */
5046 /* This is currently used only in CREATE TABLE */
5047 /* (so the permission check really isn't necessary) */
5048 ATSimplePermissions(cmd->subtype, rel,
5050 /* This command never recurses */
5052 break;
5053 case AT_AddIdentity:
5054 ATSimplePermissions(cmd->subtype, rel,
5057 /* Set up recursion for phase 2; no other prep needed */
5058 if (recurse)
5059 cmd->recurse = true;
5061 break;
5062 case AT_SetIdentity:
5063 ATSimplePermissions(cmd->subtype, rel,
5066 /* Set up recursion for phase 2; no other prep needed */
5067 if (recurse)
5068 cmd->recurse = true;
5069 /* This should run after AddIdentity, so do it in MISC pass */
5071 break;
5072 case AT_DropIdentity:
5073 ATSimplePermissions(cmd->subtype, rel,
5076 /* Set up recursion for phase 2; no other prep needed */
5077 if (recurse)
5078 cmd->recurse = true;
5080 break;
5081 case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */
5082 ATSimplePermissions(cmd->subtype, rel,
5084 /* Set up recursion for phase 2; no other prep needed */
5085 if (recurse)
5086 cmd->recurse = true;
5088 break;
5089 case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */
5090 ATSimplePermissions(cmd->subtype, rel,
5092 /* Set up recursion for phase 2; no other prep needed */
5093 if (recurse)
5094 cmd->recurse = true;
5096 break;
5097 case AT_SetExpression: /* ALTER COLUMN SET EXPRESSION */
5098 ATSimplePermissions(cmd->subtype, rel,
5100 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5102 break;
5103 case AT_DropExpression: /* ALTER COLUMN DROP EXPRESSION */
5104 ATSimplePermissions(cmd->subtype, rel,
5106 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5107 ATPrepDropExpression(rel, cmd, recurse, recursing, lockmode);
5109 break;
5110 case AT_SetStatistics: /* ALTER COLUMN SET STATISTICS */
5111 ATSimplePermissions(cmd->subtype, rel,
5114 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5115 /* No command-specific prep needed */
5117 break;
5118 case AT_SetOptions: /* ALTER COLUMN SET ( options ) */
5119 case AT_ResetOptions: /* ALTER COLUMN RESET ( options ) */
5120 ATSimplePermissions(cmd->subtype, rel,
5123 /* This command never recurses */
5125 break;
5126 case AT_SetStorage: /* ALTER COLUMN SET STORAGE */
5127 ATSimplePermissions(cmd->subtype, rel,
5130 ATSimpleRecursion(wqueue, rel, cmd, recurse, lockmode, context);
5131 /* No command-specific prep needed */
5133 break;
5134 case AT_SetCompression: /* ALTER COLUMN SET COMPRESSION */
5135 ATSimplePermissions(cmd->subtype, rel,
5137 /* This command never recurses */
5138 /* No command-specific prep needed */
5140 break;
5141 case AT_DropColumn: /* DROP COLUMN */
5142 ATSimplePermissions(cmd->subtype, rel,
5145 ATPrepDropColumn(wqueue, rel, recurse, recursing, cmd,
5146 lockmode, context);
5147 /* Recursion occurs during execution phase */
5149 break;
5150 case AT_AddIndex: /* ADD INDEX */
5152 /* This command never recurses */
5153 /* No command-specific prep needed */
5155 break;
5156 case AT_AddConstraint: /* ADD CONSTRAINT */
5157 ATSimplePermissions(cmd->subtype, rel,
5159 ATPrepAddPrimaryKey(wqueue, rel, cmd, recurse, lockmode, context);
5160 if (recurse)
5161 {
5162 /* recurses at exec time; lock descendants and set flag */
5163 (void) find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
5164 cmd->recurse = true;
5165 }
5167 break;
5168 case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
5170 /* This command never recurses */
5171 /* No command-specific prep needed */
5173 break;
5174 case AT_DropConstraint: /* DROP CONSTRAINT */
5175 ATSimplePermissions(cmd->subtype, rel,
5177 ATCheckPartitionsNotInUse(rel, lockmode);
5178 /* Other recursion occurs during execution phase */
5179 /* No command-specific prep needed except saving recurse flag */
5180 if (recurse)
5181 cmd->recurse = true;
5183 break;
5184 case AT_AlterColumnType: /* ALTER COLUMN TYPE */
5185 ATSimplePermissions(cmd->subtype, rel,
5188 /* See comments for ATPrepAlterColumnType */
5189 cmd = ATParseTransformCmd(wqueue, tab, rel, cmd, recurse, lockmode,
5190 AT_PASS_UNSET, context);
5191 Assert(cmd != NULL);
5192 /* Performs own recursion */
5193 ATPrepAlterColumnType(wqueue, tab, rel, recurse, recursing, cmd,
5194 lockmode, context);
5196 break;
5199 /* This command never recurses */
5200 /* No command-specific prep needed */
5202 break;
5203 case AT_ChangeOwner: /* ALTER OWNER */
5204 /* This command never recurses */
5205 /* No command-specific prep needed */
5207 break;
5208 case AT_ClusterOn: /* CLUSTER ON */
5209 case AT_DropCluster: /* SET WITHOUT CLUSTER */
5210 ATSimplePermissions(cmd->subtype, rel,
5212 /* These commands never recurse */
5213 /* No command-specific prep needed */
5215 break;
5216 case AT_SetLogged: /* SET LOGGED */
5217 case AT_SetUnLogged: /* SET UNLOGGED */
5219 if (tab->chgPersistence)
5220 ereport(ERROR,
5222 errmsg("cannot change persistence setting twice")));
5223 ATPrepChangePersistence(tab, rel, cmd->subtype == AT_SetLogged);
5225 break;
5226 case AT_DropOids: /* SET WITHOUT OIDS */
5227 ATSimplePermissions(cmd->subtype, rel,
5230 break;
5231 case AT_SetAccessMethod: /* SET ACCESS METHOD */
5232 ATSimplePermissions(cmd->subtype, rel,
5234
5235 /* check if another access method change was already requested */
5236 if (tab->chgAccessMethod)
5237 ereport(ERROR,
5239 errmsg("cannot have multiple SET ACCESS METHOD subcommands")));
5240
5241 ATPrepSetAccessMethod(tab, rel, cmd->name);
5242 pass = AT_PASS_MISC; /* does not matter; no work in Phase 2 */
5243 break;
5244 case AT_SetTableSpace: /* SET TABLESPACE */
5247 /* This command never recurses */
5248 ATPrepSetTableSpace(tab, rel, cmd->name, lockmode);
5249 pass = AT_PASS_MISC; /* doesn't actually matter */
5250 break;
5251 case AT_SetRelOptions: /* SET (...) */
5252 case AT_ResetRelOptions: /* RESET (...) */
5253 case AT_ReplaceRelOptions: /* reset them all, then set just these */
5254 ATSimplePermissions(cmd->subtype, rel,
5257 /* This command never recurses */
5258 /* No command-specific prep needed */
5260 break;
5261 case AT_AddInherit: /* INHERIT */
5262 ATSimplePermissions(cmd->subtype, rel,
5264 /* This command never recurses */
5267 break;
5268 case AT_DropInherit: /* NO INHERIT */
5269 ATSimplePermissions(cmd->subtype, rel,
5271 /* This command never recurses */
5274 break;
5275 case AT_AlterConstraint: /* ALTER CONSTRAINT */
5276 ATSimplePermissions(cmd->subtype, rel,
5278 /* Recursion occurs during execution phase */
5279 if (recurse)
5280 cmd->recurse = true;
5282 break;
5283 case AT_ValidateConstraint: /* VALIDATE CONSTRAINT */
5284 ATSimplePermissions(cmd->subtype, rel,
5286 /* Recursion occurs during execution phase */
5287 /* No command-specific prep needed except saving recurse flag */
5288 if (recurse)
5289 cmd->recurse = true;
5291 break;
5292 case AT_ReplicaIdentity: /* REPLICA IDENTITY ... */
5293 ATSimplePermissions(cmd->subtype, rel,
5296 /* This command never recurses */
5297 /* No command-specific prep needed */
5298 break;
5299 case AT_EnableTrig: /* ENABLE TRIGGER variants */
5302 case AT_EnableTrigAll:
5303 case AT_EnableTrigUser:
5304 case AT_DisableTrig: /* DISABLE TRIGGER variants */
5305 case AT_DisableTrigAll:
5306 case AT_DisableTrigUser:
5307 ATSimplePermissions(cmd->subtype, rel,
5309 /* Set up recursion for phase 2; no other prep needed */
5310 if (recurse)
5311 cmd->recurse = true;
5313 break;
5314 case AT_EnableRule: /* ENABLE/DISABLE RULE variants */
5317 case AT_DisableRule:
5318 case AT_AddOf: /* OF */
5319 case AT_DropOf: /* NOT OF */
5324 ATSimplePermissions(cmd->subtype, rel,
5326 /* These commands never recurse */
5327 /* No command-specific prep needed */
5329 break;
5330 case AT_GenericOptions:
5332 /* No command-specific prep needed */
5334 break;
5335 case AT_AttachPartition:
5336 ATSimplePermissions(cmd->subtype, rel,
5338 /* No command-specific prep needed */
5340 break;
5341 case AT_DetachPartition:
5343 /* No command-specific prep needed */
5345 break;
5348 /* No command-specific prep needed */
5350 break;
5351 case AT_MergePartitions:
5352 case AT_SplitPartition:
5354 /* No command-specific prep needed */
5356 break;
5357 default: /* oops */
5358 elog(ERROR, "unrecognized alter table type: %d",
5359 (int) cmd->subtype);
5360 pass = AT_PASS_UNSET; /* keep compiler quiet */
5361 break;
5362 }
5364
5365 /* Add the subcommand to the appropriate list for phase 2 */
5366 tab->subcmds[pass] = lappend(tab->subcmds[pass], cmd);
5367}
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:6958
#define ATT_INDEX
Definition tablecmds.c:341
static void ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode)
Definition tablecmds.c:8840
static void ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:9340
static void ATPrepAddColumn(List **wqueue, Relation rel, bool recurse, bool recursing, bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:7290
static void ATSimpleRecursion(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, LOCKMODE lockmode, AlterTableUtilityContext *context)
Definition tablecmds.c:6913
#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:9583
#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 9340 of file tablecmds.c.

9343{
9344 if (rel->rd_rel->reloftype && !recursing)
9345 ereport(ERROR,
9347 errmsg("cannot drop column from typed table")));
9348
9349 if (rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE)
9350 ATTypedTableRecursion(wqueue, rel, cmd, lockmode, context);
9351
9352 if (recurse)
9353 cmd->recurse = true;
9354}

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

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

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

16967{
16968 Oid amoid;
16969
16970 /*
16971 * Look up the access method name and check that it differs from the
16972 * table's current AM. If DEFAULT was specified for a partitioned table
16973 * (amname is NULL), set it to InvalidOid to reset the catalogued AM.
16974 */
16975 if (amname != NULL)
16976 amoid = get_table_am_oid(amname, false);
16977 else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
16978 amoid = InvalidOid;
16979 else
16981
16982 /* if it's a match, phase 3 doesn't need to do anything */
16983 if (rel->rd_rel->relam == amoid)
16984 return;
16985
16986 /* Save info for Phase 3 to do the real work */
16988 tab->newAccessMethod = amoid;
16989 tab->chgAccessMethod = true;
16990}
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 17090 of file tablecmds.c.

17091{
17093
17094 /* Check that the tablespace exists */
17095 tablespaceId = get_tablespace_oid(tablespacename, false);
17096
17097 /* Check permissions except when moving to database's default */
17099 {
17101
17103 if (aclresult != ACLCHECK_OK)
17104 aclcheck_error(aclresult, OBJECT_TABLESPACE, tablespacename);
17105 }
17106
17107 /* Save info for Phase 3 to do the real work */
17108 if (OidIsValid(tab->newTableSpace))
17109 ereport(ERROR,
17111 errmsg("cannot have multiple SET TABLESPACE subcommands")));
17112
17114}

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

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

6219{
6224 bool needscan = false;
6227 int i;
6228 ListCell *l;
6229 EState *estate;
6230 CommandId mycid;
6231 BulkInsertState bistate;
6232 uint32 ti_options;
6233 ExprState *partqualstate = NULL;
6234
6235 /*
6236 * Open the relation(s). We have surely already locked the existing
6237 * table.
6238 */
6239 oldrel = table_open(tab->relid, NoLock);
6240 oldTupDesc = tab->oldDesc;
6241 newTupDesc = RelationGetDescr(oldrel); /* includes all mods */
6242
6244 {
6246 false));
6248 }
6249 else
6250 newrel = NULL;
6251
6252 /*
6253 * Prepare a BulkInsertState and options for table_tuple_insert. The FSM
6254 * is empty, so don't bother using it.
6255 */
6256 if (newrel)
6257 {
6258 mycid = GetCurrentCommandId(true);
6259 bistate = GetBulkInsertState();
6260 ti_options = TABLE_INSERT_SKIP_FSM;
6261 }
6262 else
6263 {
6264 /* keep compiler quiet about using these uninitialized */
6265 mycid = 0;
6266 bistate = NULL;
6267 ti_options = 0;
6268 }
6269
6270 /*
6271 * Generate the constraint and default execution states
6272 */
6273
6274 estate = CreateExecutorState();
6275
6276 /* Build the needed expression execution states */
6277 foreach(l, tab->constraints)
6278 {
6279 NewConstraint *con = lfirst(l);
6280
6281 switch (con->contype)
6282 {
6283 case CONSTR_CHECK:
6284 needscan = true;
6286 break;
6287 case CONSTR_FOREIGN:
6288 /* Nothing to do here */
6289 break;
6290 default:
6291 elog(ERROR, "unrecognized constraint type: %d",
6292 (int) con->contype);
6293 }
6294 }
6295
6296 /* Build expression execution states for partition check quals */
6297 if (tab->partition_constraint)
6298 {
6299 needscan = true;
6300 partqualstate = ExecPrepareExpr(tab->partition_constraint, estate);
6301 }
6302
6303 foreach(l, tab->newvals)
6304 {
6305 NewColumnValue *ex = lfirst(l);
6306
6307 /* expr already planned */
6308 ex->exprstate = ExecInitExpr(ex->expr, NULL);
6309 }
6310
6312 if (newrel || tab->verify_new_notnull)
6313 {
6314 /*
6315 * If we are rebuilding the tuples OR if we added any new but not
6316 * verified not-null constraints, check all *valid* not-null
6317 * constraints. This is a bit of overkill but it minimizes risk of
6318 * bugs.
6319 *
6320 * notnull_attrs does *not* collect attribute numbers for valid
6321 * not-null constraints over virtual generated columns; instead, they
6322 * are collected in notnull_virtual_attrs for verification elsewhere.
6323 */
6324 for (i = 0; i < newTupDesc->natts; i++)
6325 {
6327
6328 if (attr->attnullability == ATTNULLABLE_VALID &&
6329 !attr->attisdropped)
6330 {
6332
6333 if (wholeatt->attgenerated != ATTRIBUTE_GENERATED_VIRTUAL)
6335 else
6337 wholeatt->attnum);
6338 }
6339 }
6341 needscan = true;
6342 }
6343
6344 if (newrel || needscan)
6345 {
6346 ExprContext *econtext;
6349 TableScanDesc scan;
6352 ListCell *lc;
6353 Snapshot snapshot;
6355
6356 /*
6357 * When adding or changing a virtual generated column with a not-null
6358 * constraint, we need to evaluate whether the generation expression
6359 * is null. For that, we borrow ExecRelGenVirtualNotNull(). Here, we
6360 * prepare a dummy ResultRelInfo.
6361 */
6363 {
6364 MemoryContext oldcontext;
6365
6366 Assert(newTupDesc->constr->has_generated_virtual);
6367 Assert(newTupDesc->constr->has_not_null);
6368 oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
6371 oldrel,
6372 0, /* dummy rangetable index */
6373 NULL,
6374 estate->es_instrument);
6375 MemoryContextSwitchTo(oldcontext);
6376 }
6377
6378 if (newrel)
6380 (errmsg_internal("rewriting table \"%s\"",
6382 else
6384 (errmsg_internal("verifying table \"%s\"",
6386
6387 if (newrel)
6388 {
6389 /*
6390 * All predicate locks on the tuples or pages are about to be made
6391 * invalid, because we move tuples around. Promote them to
6392 * relation locks.
6393 */
6395 }
6396
6397 econtext = GetPerTupleExprContext(estate);
6398
6399 /*
6400 * Create necessary tuple slots. When rewriting, two slots are needed,
6401 * otherwise one suffices. In the case where one slot suffices, we
6402 * need to use the new tuple descriptor, otherwise some constraints
6403 * can't be evaluated. Note that even when the tuple layout is the
6404 * same and no rewrite is required, the tupDescs might not be
6405 * (consider ADD COLUMN without a default).
6406 */
6407 if (tab->rewrite)
6408 {
6409 Assert(newrel != NULL);
6414
6415 /*
6416 * Set all columns in the new slot to NULL initially, to ensure
6417 * columns added as part of the rewrite are initialized to NULL.
6418 * That is necessary as tab->newvals will not contain an
6419 * expression for columns with a NULL default, e.g. when adding a
6420 * column without a default together with a column with a default
6421 * requiring an actual rewrite.
6422 */
6424 }
6425 else
6426 {
6429 newslot = NULL;
6430 }
6431
6432 /*
6433 * Any attributes that are dropped according to the new tuple
6434 * descriptor can be set to NULL. We precompute the list of dropped
6435 * attributes to avoid needing to do so in the per-tuple loop.
6436 */
6437 for (i = 0; i < newTupDesc->natts; i++)
6438 {
6439 if (TupleDescAttr(newTupDesc, i)->attisdropped)
6441 }
6442
6443 /*
6444 * Scan through the rows, generating a new row if needed and then
6445 * checking all the constraints.
6446 */
6447 snapshot = RegisterSnapshot(GetLatestSnapshot());
6448 scan = table_beginscan(oldrel, snapshot, 0, NULL,
6449 SO_NONE);
6450
6451 /*
6452 * Switch to per-tuple memory context and reset it for each tuple
6453 * produced, so we don't leak memory.
6454 */
6456
6458 {
6460
6461 if (tab->rewrite > 0)
6462 {
6463 /* Extract data from old tuple */
6466
6467 /* copy attributes */
6468 memcpy(newslot->tts_values, oldslot->tts_values,
6469 sizeof(Datum) * oldslot->tts_nvalid);
6470 memcpy(newslot->tts_isnull, oldslot->tts_isnull,
6471 sizeof(bool) * oldslot->tts_nvalid);
6472
6473 /* Set dropped attributes to null in new tuple */
6474 foreach(lc, dropped_attrs)
6475 newslot->tts_isnull[lfirst_int(lc)] = true;
6476
6477 /*
6478 * Constraints and GENERATED expressions might reference the
6479 * tableoid column, so fill tts_tableOid with the desired
6480 * value. (We must do this each time, because it gets
6481 * overwritten with newrel's OID during storing.)
6482 */
6483 newslot->tts_tableOid = RelationGetRelid(oldrel);
6484
6485 /*
6486 * Process supplied expressions to replace selected columns.
6487 *
6488 * First, evaluate expressions whose inputs come from the old
6489 * tuple.
6490 */
6491 econtext->ecxt_scantuple = oldslot;
6492
6493 foreach(l, tab->newvals)
6494 {
6495 NewColumnValue *ex = lfirst(l);
6496
6497 if (ex->is_generated)
6498 continue;
6499
6500 newslot->tts_values[ex->attnum - 1]
6501 = ExecEvalExpr(ex->exprstate,
6502 econtext,
6503 &newslot->tts_isnull[ex->attnum - 1]);
6504 }
6505
6507
6508 /*
6509 * Now, evaluate any expressions whose inputs come from the
6510 * new tuple. We assume these columns won't reference each
6511 * other, so that there's no ordering dependency.
6512 */
6513 econtext->ecxt_scantuple = newslot;
6514
6515 foreach(l, tab->newvals)
6516 {
6517 NewColumnValue *ex = lfirst(l);
6518
6519 if (!ex->is_generated)
6520 continue;
6521
6522 newslot->tts_values[ex->attnum - 1]
6523 = ExecEvalExpr(ex->exprstate,
6524 econtext,
6525 &newslot->tts_isnull[ex->attnum - 1]);
6526 }
6527
6529 }
6530 else
6531 {
6532 /*
6533 * If there's no rewrite, old and new table are guaranteed to
6534 * have the same AM, so we can just use the old slot to verify
6535 * new constraints etc.
6536 */
6538 }
6539
6540 /* Now check any constraints on the possibly-changed tuple */
6541 econtext->ecxt_scantuple = insertslot;
6542
6544 {
6546 {
6548
6549 ereport(ERROR,
6551 errmsg("column \"%s\" of relation \"%s\" contains null values",
6552 NameStr(attr->attname),
6555 }
6556 }
6557
6559 {
6561
6563 estate,
6566 {
6568
6569 ereport(ERROR,
6571 errmsg("column \"%s\" of relation \"%s\" contains null values",
6572 NameStr(attr->attname),
6575 }
6576 }
6577
6578 foreach(l, tab->constraints)
6579 {
6580 NewConstraint *con = lfirst(l);
6581
6582 switch (con->contype)
6583 {
6584 case CONSTR_CHECK:
6585 if (!ExecCheck(con->qualstate, econtext))
6586 ereport(ERROR,
6588 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
6589 con->name,
6592 break;
6593 case CONSTR_NOTNULL:
6594 case CONSTR_FOREIGN:
6595 /* Nothing to do here */
6596 break;
6597 default:
6598 elog(ERROR, "unrecognized constraint type: %d",
6599 (int) con->contype);
6600 }
6601 }
6602
6603 if (partqualstate && !ExecCheck(partqualstate, econtext))
6604 {
6605 if (tab->validate_default)
6606 ereport(ERROR,
6608 errmsg("updated partition constraint for default partition \"%s\" would be violated by some row",
6610 errtable(oldrel)));
6611 else
6612 ereport(ERROR,
6614 errmsg("partition constraint of relation \"%s\" is violated by some row",
6616 errtable(oldrel)));
6617 }
6618
6619 /* Write the tuple out to the new relation */
6620 if (newrel)
6622 ti_options, bistate);
6623
6624 ResetExprContext(econtext);
6625
6627 }
6628
6630 table_endscan(scan);
6631 UnregisterSnapshot(snapshot);
6632
6634 if (newslot)
6636 }
6637
6638 FreeExecutorState(estate);
6639
6641 if (newrel)
6642 {
6643 FreeBulkInsertState(bistate);
6644
6645 table_finish_bulk_insert(newrel, ti_options);
6646
6648 }
6649}
uint32_t uint32
Definition c.h:680
uint32 CommandId
Definition c.h:806
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:1280
AttrNumber ExecRelGenVirtualNotNull(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate, List *notnull_virtual_attrs)
Definition execMain.c:2132
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
TupleTableSlot * ExecStoreAllNullTuple(TupleTableSlot *slot)
#define ResetExprContext(econtext)
Definition executor.h:659
#define GetPerTupleMemoryContext(estate)
Definition executor.h:670
BulkInsertState GetBulkInsertState(void)
Definition heapam.c:1937
void FreeBulkInsertState(BulkInsertState bistate)
Definition heapam.c:1954
List * lappend_int(List *list, int datum)
Definition list.c:357
bool CheckRelationOidLockedByMe(Oid relid, LOCKMODE lockmode, bool orstronger)
Definition lmgr.c:351
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
#define foreach_int(var, lst)
Definition pg_list.h:502
void TransferPredicateLocksToHeapRelation(Relation relation)
Definition predicate.c:3052
int errtablecol(Relation rel, int attnum)
Definition relcache.c:6099
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 5929 of file tablecmds.c.

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

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

6837{
6838 int actual_target;
6839
6840 switch (rel->rd_rel->relkind)
6841 {
6842 case RELKIND_RELATION:
6844 break;
6847 break;
6848 case RELKIND_VIEW:
6850 break;
6851 case RELKIND_MATVIEW:
6853 break;
6854 case RELKIND_INDEX:
6856 break;
6859 break;
6862 break;
6865 break;
6866 case RELKIND_SEQUENCE:
6868 break;
6869 default:
6870 actual_target = 0;
6871 break;
6872 }
6873
6874 /* Wrong target type? */
6875 if ((actual_target & allowed_targets) == 0)
6876 {
6877 const char *action_str = alter_table_type_to_string(cmdtype);
6878
6879 if (action_str)
6880 ereport(ERROR,
6882 /* translator: %s is a group of some SQL keywords */
6883 errmsg("ALTER action %s cannot be performed on relation \"%s\"",
6885 errdetail_relkind_not_supported(rel->rd_rel->relkind)));
6886 else
6887 /* internal error? */
6888 elog(ERROR, "invalid ALTER action attempted on relation \"%s\"",
6890 }
6891
6892 /* Permissions checks */
6896
6898 ereport(ERROR,
6900 errmsg("permission denied: \"%s\" is a system catalog",
6902}
static const char * alter_table_type_to_string(AlterTableType cmdtype)
Definition tablecmds.c:6689

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, alter_table_type_to_string(), ATT_COMPOSITE_TYPE, ATT_FOREIGN_TABLE, ATT_INDEX, ATT_MATVIEW, ATT_PARTITIONED_INDEX, ATT_PARTITIONED_TABLE, ATT_SEQUENCE, ATT_TABLE, ATT_VIEW, elog, ereport, errcode(), errdetail_relkind_not_supported(), errmsg, ERROR, fb(), get_relkind_objtype(), GetUserId(), IsSystemRelation(), object_ownercheck(), RelationData::rd_rel, RelationGetRelationName, and RelationGetRelid.

Referenced by ATAddCheckNNConstraint(), ATExecAddColumn(), ATExecAddInherit(), ATExecAttachPartition(), ATExecDropColumn(), ATExecSetNotNull(), ATPrepCmd(), and dropconstraint_internal().

◆ ATSimpleRecursion()

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

Definition at line 6913 of file tablecmds.c.

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

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

21099{
21100 List *idxes;
21104 ListCell *cell;
21105 MemoryContext cxt;
21107
21109 "AttachPartitionEnsureIndexes",
21112
21117
21118 /* Build arrays of all existing indexes and their IndexInfos */
21120 {
21122
21125 }
21126
21127 /*
21128 * If we're attaching a foreign table, we must fail if any of the indexes
21129 * is a constraint index; otherwise, there's nothing to do here. Do this
21130 * before starting work, to avoid wasting the effort of building a few
21131 * non-unique indexes before coming across a unique one.
21132 */
21133 if (attachrel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
21134 {
21135 foreach(cell, idxes)
21136 {
21137 Oid idx = lfirst_oid(cell);
21139
21140 if (idxRel->rd_index->indisunique ||
21141 idxRel->rd_index->indisprimary)
21142 ereport(ERROR,
21144 errmsg("cannot attach foreign table \"%s\" as partition of partitioned table \"%s\"",
21147 errdetail("Partitioned table \"%s\" contains unique indexes.",
21150 }
21151
21152 goto out;
21153 }
21154
21155 /*
21156 * For each index on the partitioned table, find a matching one in the
21157 * partition-to-be; if one is not found, create one.
21158 */
21159 foreach(cell, idxes)
21160 {
21161 Oid idx = lfirst_oid(cell);
21163 IndexInfo *info;
21164 AttrMap *attmap;
21165 bool found = false;
21167
21168 /*
21169 * Ignore indexes in the partitioned table other than partitioned
21170 * indexes.
21171 */
21172 if (idxRel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
21173 {
21175 continue;
21176 }
21177
21178 /* construct an indexinfo to compare existing indexes against */
21179 info = BuildIndexInfo(idxRel);
21181 RelationGetDescr(rel),
21182 false);
21184
21185 /*
21186 * Scan the list of existing indexes in the partition-to-be, and mark
21187 * the first matching, valid, unattached one we find, if any, as
21188 * partition of the parent index. If we find one, we're done.
21189 */
21190 for (int i = 0; i < list_length(attachRelIdxs); i++)
21191 {
21194
21195 /* does this index have a parent? if so, can't use it */
21196 if (attachrelIdxRels[i]->rd_rel->relispartition)
21197 continue;
21198
21199 /* If this index is invalid, can't use it */
21200 if (!attachrelIdxRels[i]->rd_index->indisvalid)
21201 continue;
21202
21203 if (CompareIndexInfo(attachInfos[i], info,
21204 attachrelIdxRels[i]->rd_indcollation,
21205 idxRel->rd_indcollation,
21206 attachrelIdxRels[i]->rd_opfamily,
21207 idxRel->rd_opfamily,
21208 attmap))
21209 {
21210 /*
21211 * If this index is being created in the parent because of a
21212 * constraint, then the child needs to have a constraint also,
21213 * so look for one. If there is no such constraint, this
21214 * index is no good, so keep looking.
21215 */
21217 {
21218 cldConstrOid =
21220 cldIdxId);
21221 /* no dice */
21223 continue;
21224
21225 /* Ensure they're both the same type of constraint */
21228 continue;
21229 }
21230
21231 /* bingo. */
21236 found = true;
21237
21239 break;
21240 }
21241 }
21242
21243 /*
21244 * If no suitable index was found in the partition-to-be, create one
21245 * now. Note that if this is a PK, not-null constraints must already
21246 * exist.
21247 */
21248 if (!found)
21249 {
21250 IndexStmt *stmt;
21251 Oid conOid;
21252
21254 idxRel, attmap,
21255 &conOid);
21259 conOid,
21260 -1,
21261 true, false, false, false, false);
21262 }
21263
21265 }
21266
21267out:
21268 /* Clean up. */
21269 for (int i = 0; i < list_length(attachRelIdxs); i++)
21273}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:263
char get_constraint_type(Oid conoid)
Definition lsyscache.c:1361
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:475
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
IndexStmt * generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx, const AttrMap *attmap, Oid *constraintOid)
#define foreach_current_index(var_or_cell)
Definition pg_list.h:435

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

Referenced by attachPartitionTable().

◆ AttachPartitionForeignKey()

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

Definition at line 11875 of file tablecmds.c.

11882{
11887 bool queueValidation;
11891
11892 /* Fetch the parent constraint tuple */
11896 elog(ERROR, "cache lookup failed for constraint %u", parentConstrOid);
11898 parentConstrIsEnforced = parentConstr->conenforced;
11899
11900 /* Fetch the child constraint tuple */
11904 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11906 partConstrFrelid = partConstr->confrelid;
11907 partConstrRelid = partConstr->conrelid;
11908
11909 /*
11910 * If the referenced table is partitioned, then the partition we're
11911 * attaching now has extra pg_constraint rows and action triggers that are
11912 * no longer needed. Remove those.
11913 */
11915 {
11917
11920
11922 }
11923
11924 /*
11925 * Will we need to validate this constraint? A valid parent constraint
11926 * implies that all child constraints have been validated, so if this one
11927 * isn't, we must trigger phase 3 validation.
11928 */
11929 queueValidation = parentConstr->convalidated && !partConstr->convalidated;
11930
11933
11934 /*
11935 * The action triggers in the new partition become redundant -- the parent
11936 * table already has equivalent ones, and those will be able to reach the
11937 * partition. Remove the ones in the partition. We identify them because
11938 * they have our constraint OID, as well as being on the referenced rel.
11939 */
11942
11945
11946 /*
11947 * Like the constraint, attach partition's "check" triggers to the
11948 * corresponding parent triggers if the constraint is ENFORCED. NOT
11949 * ENFORCED constraints do not have these triggers.
11950 */
11952 {
11955
11965 }
11966
11967 /*
11968 * We updated this pg_constraint row above to set its parent; validating
11969 * it will cause its convalidated flag to change, so we need CCI here. In
11970 * addition, we need it unconditionally for the rare case where the parent
11971 * table has *two* identical constraints; when reaching this function for
11972 * the second one, we must have made our changes visible, otherwise we
11973 * would try to attach both to this one.
11974 */
11976
11977 /* If validation is needed, put it in the queue now. */
11978 if (queueValidation)
11979 {
11981 Oid confrelid;
11982
11984
11987 elog(ERROR, "cache lookup failed for constraint %u", partConstrOid);
11988
11989 confrelid = ((Form_pg_constraint) GETSTRUCT(partcontup))->confrelid;
11990
11991 /* Use the same lock as for AT_ValidateConstraint */
11996 }
11997}
static void RemoveInheritedConstraint(Relation conrel, Relation trigrel, Oid conoid, Oid conrelid)
static void GetForeignKeyCheckTriggers(Relation trigrel, Oid conoid, Oid confrelid, Oid conrelid, Oid *insertTriggerOid, Oid *updateTriggerOid)
void TriggerSetParentTrigger(Relation trigRel, Oid childTrigId, Oid parentTrigId, Oid childTableId)
Definition trigger.c:1222

References Assert, CommandCounterIncrement(), ConstraintSetParentConstraint(), DropForeignKeyConstraintTriggers(), elog, ERROR, fb(), Form_pg_constraint, get_rel_relkind(), GetForeignKeyCheckTriggers(), GETSTRUCT(), HeapTupleIsValid, ObjectIdGetDatum(), OidIsValid, QueueFKConstraintValidation(), RelationGetRelid, ReleaseSysCache(), RemoveInheritedConstraint(), RowExclusiveLock, RowShareLock, SearchSysCache1(), ShareUpdateExclusiveLock, table_close(), table_open(), and TriggerSetParentTrigger().

Referenced by tryAttachPartitionForeignKey().

◆ attachPartitionTable()

static void attachPartitionTable ( List **  wqueue,
Relation  rel,
Relation  attachrel,
PartitionBoundSpec bound 
)
static

Definition at line 20725 of file tablecmds.c.

20726{
20727 /*
20728 * Create an inheritance; the relevant checks are performed inside the
20729 * function.
20730 */
20731 CreateInheritance(attachrel, rel, true);
20732
20733 /* Update the pg_class entry. */
20734 StorePartitionBound(attachrel, rel, bound);
20735
20736 /* Ensure there exists a correct set of indexes in the partition. */
20738
20739 /* and triggers */
20741
20742 /*
20743 * Clone foreign key constraints. Callee is responsible for setting up
20744 * for phase 3 constraint verification.
20745 */
20747}
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 6988 of file tablecmds.c.

6990{
6991 ListCell *child;
6992 List *children;
6993
6994 Assert(rel->rd_rel->relkind == RELKIND_COMPOSITE_TYPE);
6995
6996 children = find_typed_table_dependencies(rel->rd_rel->reltype,
6998 cmd->behavior);
6999
7000 foreach(child, children)
7001 {
7002 Oid childrelid = lfirst_oid(child);
7004
7005 childrel = relation_open(childrelid, lockmode);
7007 ATPrepCmd(wqueue, childrel, cmd, true, true, lockmode, context);
7009 }
7010}
static List * find_typed_table_dependencies(Oid typeOid, const char *typeName, DropBehavior behavior)
Definition tablecmds.c:7191

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

1437{
1438 int natts;
1440 ListCell *l;
1441 TupleDesc desc;
1442 char *attname;
1443 Oid atttypid;
1444 int32 atttypmod;
1445 Oid attcollation;
1446 int attdim;
1447
1448 /*
1449 * allocate a new tuple descriptor
1450 */
1451 natts = list_length(columns);
1452 desc = CreateTemplateTupleDesc(natts);
1453
1454 attnum = 0;
1455
1456 foreach(l, columns)
1457 {
1458 ColumnDef *entry = lfirst(l);
1461
1462 /*
1463 * for each entry in the list, get the name and type information from
1464 * the list and have TupleDescInitEntry fill in the attribute
1465 * information we need.
1466 */
1467 attnum++;
1468
1469 attname = entry->colname;
1470 typenameTypeIdAndMod(NULL, entry->typeName, &atttypid, &atttypmod);
1471
1473 if (aclresult != ACLCHECK_OK)
1475
1476 attcollation = GetColumnDefCollation(NULL, entry, atttypid);
1478 if (attdim > PG_INT16_MAX)
1479 ereport(ERROR,
1481 errmsg("too many array dimensions"));
1482
1483 if (entry->typeName->setof)
1484 ereport(ERROR,
1486 errmsg("column \"%s\" cannot be declared SETOF",
1487 attname)));
1488
1490 atttypid, atttypmod, attdim);
1491 att = TupleDescAttr(desc, attnum - 1);
1492
1493 /* Override TupleDescInitEntry's settings as requested */
1494 TupleDescInitEntryCollation(desc, attnum, attcollation);
1495
1496 /* Fill in additional stuff not handled by TupleDescInitEntry */
1497 att->attnotnull = entry->is_not_null;
1498 att->attislocal = entry->is_local;
1499 att->attinhcount = entry->inhcount;
1500 att->attidentity = entry->identity;
1501 att->attgenerated = entry->generated;
1502 att->attcompression = GetAttributeCompression(att->atttypid, entry->compression);
1503 if (entry->storage)
1504 att->attstorage = entry->storage;
1505 else if (entry->storage_name)
1506 att->attstorage = GetAttributeStorage(att->atttypid, entry->storage_name);
1507
1509 }
1510
1511 TupleDescFinalize(desc);
1512
1513 return desc;
1514}
NameData attname
bool is_not_null
Definition parsenodes.h:777
char identity
Definition parsenodes.h:783
char * storage_name
Definition parsenodes.h:780
char * colname
Definition parsenodes.h:772
TypeName * typeName
Definition parsenodes.h:773
char generated
Definition parsenodes.h:786
char storage
Definition parsenodes.h:779
bool is_local
Definition parsenodes.h:776
int16 inhcount
Definition parsenodes.h:775
char * compression
Definition parsenodes.h:774
bool setof
Definition parsenodes.h:292
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 22607 of file tablecmds.c.

22608{
22609 /*
22610 * Build the needed expression execution states. Here, we expect only NOT
22611 * NULL and CHECK constraint.
22612 */
22614 {
22615 switch (con->contype)
22616 {
22617 case CONSTR_CHECK:
22618
22619 /*
22620 * We already expanded virtual expression in
22621 * createTableConstraints.
22622 */
22623 con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
22624 break;
22625 case CONSTR_NOTNULL:
22626 /* Nothing to do here. */
22627 break;
22628 default:
22629 elog(ERROR, "unrecognized constraint type: %d",
22630 (int) con->contype);
22631 }
22632 }
22633
22634 /* Expression already planned in createTableConstraints */
22636 ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
22637}

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

16789{
16791 SysScanDesc scan;
16792 ScanKeyData key[1];
16794
16796 ScanKeyInit(&key[0],
16799 ObjectIdGetDatum(relationOid));
16801 true, NULL, 1, key);
16803 {
16808 Acl *newAcl;
16810 bool isNull;
16811 HeapTuple newtuple;
16812
16813 /* Ignore dropped columns */
16814 if (att->attisdropped)
16815 continue;
16816
16820 &isNull);
16821 /* Null ACLs do not require changes */
16822 if (isNull)
16823 continue;
16824
16825 memset(repl_null, false, sizeof(repl_null));
16826 memset(repl_repl, false, sizeof(repl_repl));
16827
16832
16836
16837 CatalogTupleUpdate(attRelation, &newtuple->t_self, newtuple);
16838
16839 heap_freetuple(newtuple);
16840 }
16841 systable_endscan(scan);
16843}

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

16854{
16856 SysScanDesc scan;
16857 ScanKeyData key[2];
16858 HeapTuple tup;
16859
16860 /*
16861 * SERIAL sequences are those having an auto dependency on one of the
16862 * table's columns (we don't care *which* column, exactly).
16863 */
16865
16866 ScanKeyInit(&key[0],
16870 ScanKeyInit(&key[1],
16873 ObjectIdGetDatum(relationOid));
16874 /* we leave refobjsubid unspecified */
16875
16877 NULL, 2, key);
16878
16879 while (HeapTupleIsValid(tup = systable_getnext(scan)))
16880 {
16883
16884 /* skip dependencies other than auto dependencies on columns */
16885 if (depForm->refobjsubid == 0 ||
16886 depForm->classid != RelationRelationId ||
16887 depForm->objsubid != 0 ||
16888 !(depForm->deptype == DEPENDENCY_AUTO || depForm->deptype == DEPENDENCY_INTERNAL))
16889 continue;
16890
16891 /* Use relation_open just in case it's an index */
16892 seqRel = relation_open(depForm->objid, lockmode);
16893
16894 /* skip non-sequence relations */
16895 if (RelationGetForm(seqRel)->relkind != RELKIND_SEQUENCE)
16896 {
16897 /* No need to keep the lock */
16898 relation_close(seqRel, lockmode);
16899 continue;
16900 }
16901
16902 /* We don't need to close the sequence while we alter it. */
16903 ATExecChangeOwner(depForm->objid, newOwnerId, true, lockmode);
16904
16905 /* Now we can close it. Keep the lock till end of transaction. */
16907 }
16908
16909 systable_endscan(scan);
16910
16912}

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

7745{
7747 int attnum;
7748
7749 /*
7750 * this test is deliberately not attisdropped-aware, since if one tries to
7751 * add a column matching a dropped column name, it's gonna fail anyway.
7752 */
7755 PointerGetDatum(colname));
7757 return true;
7758
7761
7762 /*
7763 * We throw a different error message for conflicts with system column
7764 * names, since they are normally not shown and the user might otherwise
7765 * be confused about the reason for the conflict.
7766 */
7767 if (attnum <= 0)
7768 ereport(ERROR,
7770 errmsg("column name \"%s\" conflicts with a system column name",
7771 colname)));
7772 else
7773 {
7774 if (if_not_exists)
7775 {
7778 errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
7779 colname, RelationGetRelationName(rel))));
7780 return false;
7781 }
7782
7783 ereport(ERROR,
7785 errmsg("column \"%s\" of relation \"%s\" already exists",
7786 colname, RelationGetRelationName(rel))));
7787 }
7788
7789 return true;
7790}
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 7240 of file tablecmds.c.

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

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

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

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

14140{
14141 Oid roleid = GetUserId();
14143 int i;
14144
14145 /* Okay if we have relation-level REFERENCES permission */
14148 if (aclresult == ACLCHECK_OK)
14149 return;
14150 /* Else we must have REFERENCES on each column */
14151 for (i = 0; i < natts; i++)
14152 {
14154 roleid, ACL_REFERENCES);
14155 if (aclresult != ACLCHECK_OK)
14158 }
14159}
AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum, Oid roleid, AclMode mode)
Definition aclchk.c:3912
AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
Definition aclchk.c:4083
#define ACL_REFERENCES
Definition parsenodes.h:81

References ACL_REFERENCES, aclcheck_error(), ACLCHECK_OK, fb(), get_relkind_objtype(), GetUserId(), i, pg_attribute_aclcheck(), pg_class_aclcheck(), RelationData::rd_rel, RelationGetRelationName, and RelationGetRelid.

Referenced by ATAddForeignKeyConstraint().

◆ CheckRelationTableSpaceMove()

bool CheckRelationTableSpaceMove ( Relation  rel,
Oid  newTableSpaceId 
)

Definition at line 3757 of file tablecmds.c.

3758{
3760
3761 /*
3762 * No work if no change in tablespace. Note that MyDatabaseTableSpace is
3763 * stored as 0.
3764 */
3765 oldTableSpaceId = rel->rd_rel->reltablespace;
3768 return false;
3769
3770 /*
3771 * We cannot support moving mapped relations into different tablespaces.
3772 * (In particular this eliminates all shared catalogs.)
3773 */
3774 if (RelationIsMapped(rel))
3775 ereport(ERROR,
3777 errmsg("cannot move system relation \"%s\"",
3779
3780 /* Cannot move a non-shared relation into pg_global */
3782 ereport(ERROR,
3784 errmsg("only shared relations can be placed in pg_global tablespace")));
3785
3786 /*
3787 * Do not allow moving temp tables of other backends ... their local
3788 * buffer manager is not going to cope.
3789 */
3790 if (RELATION_IS_OTHER_TEMP(rel))
3791 ereport(ERROR,
3793 errmsg("cannot move temporary tables of other sessions")));
3794
3795 return true;
3796}
#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 4480 of file tablecmds.c.

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

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

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

◆ ChooseForeignKeyConstraintNameAddition()

static char * ChooseForeignKeyConstraintNameAddition ( List colnames)
static

Definition at line 9955 of file tablecmds.c.

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

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

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

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

11304{
11305 /* This only works for declarative partitioning */
11306 Assert(parentRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
11307
11308 /*
11309 * First, clone constraints where the parent is on the referencing side.
11310 */
11312
11313 /*
11314 * Clone constraints for which the parent is on the referenced side.
11315 */
11317}
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 21281 of file tablecmds.c.

21282{
21285 SysScanDesc scan;
21286 HeapTuple tuple;
21288
21293 true, NULL, 1, &key);
21294
21296 "clone trig", ALLOCSET_SMALL_SIZES);
21297
21298 while (HeapTupleIsValid(tuple = systable_getnext(scan)))
21299 {
21302 Node *qual = NULL;
21303 Datum value;
21304 bool isnull;
21305 List *cols = NIL;
21306 List *trigargs = NIL;
21308
21309 /*
21310 * Ignore statement-level triggers; those are not cloned.
21311 */
21312 if (!TRIGGER_FOR_ROW(trigForm->tgtype))
21313 continue;
21314
21315 /*
21316 * Don't clone internal triggers, because the constraint cloning code
21317 * will.
21318 */
21319 if (trigForm->tgisinternal)
21320 continue;
21321
21322 /*
21323 * Complain if we find an unexpected trigger type.
21324 */
21325 if (!TRIGGER_FOR_BEFORE(trigForm->tgtype) &&
21326 !TRIGGER_FOR_AFTER(trigForm->tgtype))
21327 elog(ERROR, "unexpected trigger \"%s\" found",
21328 NameStr(trigForm->tgname));
21329
21330 /* Use short-lived context for CREATE TRIGGER */
21332
21333 /*
21334 * If there is a WHEN clause, generate a 'cooked' version of it that's
21335 * appropriate for the partition.
21336 */
21338 RelationGetDescr(pg_trigger), &isnull);
21339 if (!isnull)
21340 {
21342 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_OLD_VARNO,
21343 partition, parent);
21344 qual = (Node *) map_partition_varattnos((List *) qual, PRS2_NEW_VARNO,
21345 partition, parent);
21346 }
21347
21348 /*
21349 * If there is a column list, transform it to a list of column names.
21350 * Note we don't need to map this list in any way ...
21351 */
21352 if (trigForm->tgattr.dim1 > 0)
21353 {
21354 int i;
21355
21356 for (i = 0; i < trigForm->tgattr.dim1; i++)
21357 {
21359
21360 col = TupleDescAttr(parent->rd_att,
21361 trigForm->tgattr.values[i] - 1);
21362 cols = lappend(cols,
21363 makeString(pstrdup(NameStr(col->attname))));
21364 }
21365 }
21366
21367 /* Reconstruct trigger arguments list. */
21368 if (trigForm->tgnargs > 0)
21369 {
21370 char *p;
21371
21373 RelationGetDescr(pg_trigger), &isnull);
21374 if (isnull)
21375 elog(ERROR, "tgargs is null for trigger \"%s\" in partition \"%s\"",
21377
21378 p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
21379
21380 for (int i = 0; i < trigForm->tgnargs; i++)
21381 {
21383 p += strlen(p) + 1;
21384 }
21385 }
21386
21388 trigStmt->replace = false;
21389 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint);
21390 trigStmt->trigname = NameStr(trigForm->tgname);
21391 trigStmt->relation = NULL;
21392 trigStmt->funcname = NULL; /* passed separately */
21393 trigStmt->args = trigargs;
21394 trigStmt->row = true;
21395 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK;
21396 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK;
21397 trigStmt->columns = cols;
21398 trigStmt->whenClause = NULL; /* passed separately */
21399 trigStmt->transitionRels = NIL; /* not supported at present */
21400 trigStmt->deferrable = trigForm->tgdeferrable;
21401 trigStmt->initdeferred = trigForm->tginitdeferred;
21402 trigStmt->constrrel = NULL; /* passed separately */
21403
21405 trigForm->tgconstrrelid, InvalidOid, InvalidOid,
21406 trigForm->tgfoid, trigForm->oid, qual,
21407 false, true, trigForm->tgenabled);
21408
21411 }
21412
21414
21415 systable_endscan(scan);
21417}
#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 23300 of file tablecmds.c.

23301{
23304
23305 if (ea->parentIndexOid != eb->parentIndexOid)
23306 return pg_cmp_u32(ea->parentIndexOid, eb->parentIndexOid);
23307 return pg_cmp_u32(ea->indexOid, eb->indexOid);
23308}
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 23328 of file tablecmds.c.

23329{
23330 List *collected = NIL;
23331 List *result = NIL;
23333
23334 /*
23335 * Phase 1: collect one entry per (partition index -> parent index) pair,
23336 * with its extension dependency OIDs sorted ascending.
23337 */
23339 {
23340 Relation partRel;
23341 List *indexList;
23342
23343 /*
23344 * Use NoLock since the caller already holds AccessExclusiveLock on
23345 * these partitions.
23346 */
23347 partRel = table_open(partOid, NoLock);
23349
23350 foreach_oid(indexOid, indexList)
23351 {
23352 Oid parentIndexOid;
23354
23355 if (!get_rel_relispartition(indexOid))
23356 continue;
23357
23358 parentIndexOid = get_partition_parent(indexOid, true);
23359 if (!OidIsValid(parentIndexOid))
23360 continue;
23361
23362 entry = palloc(sizeof(PartitionIndexExtDepEntry));
23363 entry->parentIndexOid = parentIndexOid;
23364 entry->indexOid = indexOid;
23366 indexOid);
23368
23369 collected = lappend(collected, entry);
23370 }
23371
23373 table_close(partRel, NoLock);
23374 }
23375
23376 /*
23377 * Phase 2: sort by parentIndexOid so entries sharing a parent index sit
23378 * adjacent.
23379 */
23381
23382 /*
23383 * Phase 3: single linear pass verifying that adjacent entries sharing a
23384 * parent index have identical extension dependencies, and keeping one
23385 * representative entry per parent index.
23386 */
23388 {
23389 if (prev != NULL && prev->parentIndexOid == entry->parentIndexOid)
23390 {
23391 if (!equal_oid_lists(prev->extensionOids, entry->extensionOids))
23392 ereport(ERROR,
23394 errmsg("cannot merge partitions with conflicting extension dependencies"),
23395 errdetail("Partition indexes \"%s\" and \"%s\" depend on different extensions.",
23396 get_rel_name(prev->indexOid),
23397 get_rel_name(entry->indexOid))));
23398
23399 /* Duplicate entry for the same parent index; discard. */
23400 list_free(entry->extensionOids);
23401 pfree(entry);
23402 continue;
23403 }
23404
23405 result = lappend(result, entry);
23406 prev = entry;
23407 }
23408
23410
23411 return result;
23412}
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 20256 of file tablecmds.c.

20259{
20260 int attn;
20261 ListCell *lc;
20262 Oid am_oid;
20263
20264 attn = 0;
20265 foreach(lc, partParams)
20266 {
20268 Oid atttype;
20269 Oid attcollation;
20270
20271 if (pelem->name != NULL)
20272 {
20273 /* Simple attribute reference */
20276
20278 pelem->name);
20280 ereport(ERROR,
20282 errmsg("column \"%s\" named in partition key does not exist",
20283 pelem->name),
20284 parser_errposition(pstate, pelem->location)));
20286
20287 if (attform->attnum <= 0)
20288 ereport(ERROR,
20290 errmsg("cannot use system column \"%s\" in partition key",
20291 pelem->name),
20292 parser_errposition(pstate, pelem->location)));
20293
20294 /*
20295 * Stored generated columns cannot work: They are computed after
20296 * BEFORE triggers, but partition routing is done before all
20297 * triggers. Maybe virtual generated columns could be made to
20298 * work, but then they would need to be handled as an expression
20299 * below.
20300 */
20301 if (attform->attgenerated)
20302 ereport(ERROR,
20304 errmsg("cannot use generated column in partition key"),
20305 errdetail("Column \"%s\" is a generated column.",
20306 pelem->name),
20307 parser_errposition(pstate, pelem->location)));
20308
20309 partattrs[attn] = attform->attnum;
20310 atttype = attform->atttypid;
20311 attcollation = attform->attcollation;
20313 }
20314 else
20315 {
20316 /* Expression */
20317 Node *expr = pelem->expr;
20318 char partattname[16];
20320 int i;
20321
20322 Assert(expr != NULL);
20323 atttype = exprType(expr);
20324 attcollation = exprCollation(expr);
20325
20326 /*
20327 * The expression must be of a storable type (e.g., not RECORD).
20328 * The test is the same as for whether a table column is of a safe
20329 * type (which is why we needn't check for the non-expression
20330 * case).
20331 */
20332 snprintf(partattname, sizeof(partattname), "%d", attn + 1);
20334 atttype, attcollation,
20336
20337 /*
20338 * Strip any top-level COLLATE clause. This ensures that we treat
20339 * "x COLLATE y" and "(x COLLATE y)" alike.
20340 */
20341 while (IsA(expr, CollateExpr))
20342 expr = (Node *) ((CollateExpr *) expr)->arg;
20343
20344 /*
20345 * Examine all the columns in the partition key expression. When
20346 * the whole-row reference is present, examine all the columns of
20347 * the partitioned table.
20348 */
20349 pull_varattnos(expr, 1, &expr_attrs);
20351 {
20356 }
20357
20358 i = -1;
20359 while ((i = bms_next_member(expr_attrs, i)) >= 0)
20360 {
20362
20363 Assert(attno != 0);
20364
20365 /*
20366 * Cannot allow system column references, since that would
20367 * make partition routing impossible: their values won't be
20368 * known yet when we need to do that.
20369 */
20370 if (attno < 0)
20371 ereport(ERROR,
20373 errmsg("partition key expressions cannot contain system column references")));
20374
20375 /*
20376 * Stored generated columns cannot work: They are computed
20377 * after BEFORE triggers, but partition routing is done before
20378 * all triggers. Virtual generated columns could probably
20379 * work, but it would require more work elsewhere (for example
20380 * SET EXPRESSION would need to check whether the column is
20381 * used in partition keys). Seems safer to prohibit for now.
20382 */
20383 if (TupleDescAttr(RelationGetDescr(rel), attno - 1)->attgenerated)
20384 ereport(ERROR,
20386 errmsg("cannot use generated column in partition key"),
20387 errdetail("Column \"%s\" is a generated column.",
20388 get_attname(RelationGetRelid(rel), attno, false)),
20389 parser_errposition(pstate, pelem->location)));
20390 }
20391
20392 if (IsA(expr, Var) &&
20393 ((Var *) expr)->varattno > 0)
20394 {
20395
20396 /*
20397 * User wrote "(column)" or "(column COLLATE something)".
20398 * Treat it like simple attribute anyway.
20399 */
20400 partattrs[attn] = ((Var *) expr)->varattno;
20401 }
20402 else
20403 {
20404 partattrs[attn] = 0; /* marks the column as expression */
20405 *partexprs = lappend(*partexprs, expr);
20406
20407 /*
20408 * transformPartitionSpec() should have already rejected
20409 * subqueries, aggregates, window functions, and SRFs, based
20410 * on the EXPR_KIND_ for partition expressions.
20411 */
20412
20413 /*
20414 * Preprocess the expression before checking for mutability.
20415 * This is essential for the reasons described in
20416 * contain_mutable_functions_after_planning. However, we call
20417 * expression_planner for ourselves rather than using that
20418 * function, because if constant-folding reduces the
20419 * expression to a constant, we'd like to know that so we can
20420 * complain below.
20421 *
20422 * Like contain_mutable_functions_after_planning, assume that
20423 * expression_planner won't scribble on its input, so this
20424 * won't affect the partexprs entry we saved above.
20425 */
20426 expr = (Node *) expression_planner((Expr *) expr);
20427
20428 /*
20429 * Partition expressions cannot contain mutable functions,
20430 * because a given row must always map to the same partition
20431 * as long as there is no change in the partition boundary
20432 * structure.
20433 */
20434 if (contain_mutable_functions(expr))
20435 ereport(ERROR,
20437 errmsg("functions in partition key expression must be marked IMMUTABLE")));
20438
20439 /*
20440 * While it is not exactly *wrong* for a partition expression
20441 * to be a constant, it seems better to reject such keys.
20442 */
20443 if (IsA(expr, Const))
20444 ereport(ERROR,
20446 errmsg("cannot use constant expression as partition key")));
20447 }
20448 }
20449
20450 /*
20451 * Apply collation override if any
20452 */
20453 if (pelem->collation)
20454 attcollation = get_collation_oid(pelem->collation, false);
20455
20456 /*
20457 * Check we have a collation iff it's a collatable type. The only
20458 * expected failures here are (1) COLLATE applied to a noncollatable
20459 * type, or (2) partition expression had an unresolved collation. But
20460 * we might as well code this to be a complete consistency check.
20461 */
20462 if (type_is_collatable(atttype))
20463 {
20464 if (!OidIsValid(attcollation))
20465 ereport(ERROR,
20467 errmsg("could not determine which collation to use for partition expression"),
20468 errhint("Use the COLLATE clause to set the collation explicitly.")));
20469 }
20470 else
20471 {
20472 if (OidIsValid(attcollation))
20473 ereport(ERROR,
20475 errmsg("collations are not supported by type %s",
20476 format_type_be(atttype))));
20477 }
20478
20479 partcollation[attn] = attcollation;
20480
20481 /*
20482 * Identify the appropriate operator class. For list and range
20483 * partitioning, we use a btree operator class; hash partitioning uses
20484 * a hash operator class.
20485 */
20486 if (strategy == PARTITION_STRATEGY_HASH)
20487 am_oid = HASH_AM_OID;
20488 else
20489 am_oid = BTREE_AM_OID;
20490
20491 if (!pelem->opclass)
20492 {
20493 partopclass[attn] = GetDefaultOpClass(atttype, am_oid);
20494
20496 {
20497 if (strategy == PARTITION_STRATEGY_HASH)
20498 ereport(ERROR,
20500 errmsg("data type %s has no default operator class for access method \"%s\"",
20501 format_type_be(atttype), "hash"),
20502 errhint("You must specify a hash operator class or define a default hash operator class for the data type.")));
20503 else
20504 ereport(ERROR,
20506 errmsg("data type %s has no default operator class for access method \"%s\"",
20507 format_type_be(atttype), "btree"),
20508 errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
20509 }
20510 }
20511 else
20513 atttype,
20514 am_oid == HASH_AM_OID ? "hash" : "btree",
20515 am_oid);
20516
20517 attn++;
20518 }
20519}
int bms_next_member(const Bitmapset *a, int prevbit)
Definition bitmapset.c:1290
Bitmapset * bms_add_range(Bitmapset *a, int lower, int upper)
Definition bitmapset.c:1003
Bitmapset * bms_del_member(Bitmapset *a, int x)
Definition bitmapset.c:852
bool bms_is_member(int x, const Bitmapset *a)
Definition bitmapset.c:510
bool contain_mutable_functions(Node *clause)
Definition clauses.c:383
#define CHKATYPE_IS_PARTKEY
Definition heap.h:25
Oid GetDefaultOpClass(Oid type_id, Oid am_id)
Definition indexcmds.c:2371
Oid ResolveOpClass(const List *opclass, Oid attrType, const char *accessMethodName, Oid accessMethodId)
Definition indexcmds.c:2286
bool type_is_collatable(Oid typid)
Definition lsyscache.c:3389
Oid get_collation_oid(List *collname, bool missing_ok)
Definition namespace.c:4043
Oid exprCollation(const Node *expr)
Definition nodeFuncs.c:826
@ PARTITION_STRATEGY_HASH
Definition parsenodes.h: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 20585 of file tablecmds.c.

20586{
20588 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20589 int num_check,
20590 i;
20591
20592 num_check = (constr != NULL) ? constr->num_check : 0;
20593 for (i = 0; i < num_check; i++)
20594 {
20595 Node *cexpr;
20596
20597 /*
20598 * If this constraint hasn't been fully validated yet, we must ignore
20599 * it here.
20600 */
20601 if (!constr->check[i].ccvalid)
20602 continue;
20603
20604 /*
20605 * NOT ENFORCED constraints are always marked as invalid, which should
20606 * have been ignored.
20607 */
20608 Assert(constr->check[i].ccenforced);
20609
20610 cexpr = stringToNode(constr->check[i].ccbin);
20611
20612 /*
20613 * Run each expression through const-simplification and
20614 * canonicalization. It is necessary, because we will be comparing it
20615 * to similarly-processed partition constraint expressions, and may
20616 * fail to detect valid matches without this.
20617 */
20618 cexpr = eval_const_expressions(NULL, cexpr);
20619 cexpr = (Node *) canonicalize_qual((Expr *) cexpr, true);
20620
20622 make_ands_implicit((Expr *) cexpr));
20623 }
20624
20625 /*
20626 * Try to make the proof. Since we are comparing CHECK constraints, we
20627 * need to use weak implication, i.e., we assume existConstraint is
20628 * not-false and try to prove the same for testConstraint.
20629 *
20630 * Note that predicate_implied_by assumes its first argument is known
20631 * immutable. That should always be true for both NOT NULL and partition
20632 * constraints, so we don't test it here.
20633 */
20635}
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 17942 of file tablecmds.c.

17943{
17946
17947 if (acon->condeferrable != bcon->condeferrable ||
17948 acon->condeferred != bcon->condeferred ||
17951 return false;
17952 else
17953 return true;
17954}
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 14268 of file tablecmds.c.

14271{
14274
14275 /*
14276 * Note: for a self-referential FK (referencing and referenced tables are
14277 * the same), it is important that the ON UPDATE action fires before the
14278 * CHECK action, since both triggers will fire on the same row during an
14279 * UPDATE event; otherwise the CHECK trigger will be checking a non-final
14280 * state of the row. Triggers fire in name order, so we ensure this by
14281 * using names like "RI_ConstraintTrigger_a_NNNN" for the action triggers
14282 * and "RI_ConstraintTrigger_c_NNNN" for the check triggers.
14283 */
14285 fk_trigger->replace = false;
14286 fk_trigger->isconstraint = true;
14287 fk_trigger->trigname = "RI_ConstraintTrigger_c";
14288 fk_trigger->relation = NULL;
14289
14290 /* Either ON INSERT or ON UPDATE */
14291 if (on_insert)
14292 {
14293 fk_trigger->funcname = SystemFuncName("RI_FKey_check_ins");
14295 }
14296 else
14297 {
14298 fk_trigger->funcname = SystemFuncName("RI_FKey_check_upd");
14300 }
14301
14302 fk_trigger->args = NIL;
14303 fk_trigger->row = true;
14305 fk_trigger->columns = NIL;
14306 fk_trigger->whenClause = NULL;
14307 fk_trigger->transitionRels = NIL;
14308 fk_trigger->deferrable = fkconstraint->deferrable;
14309 fk_trigger->initdeferred = fkconstraint->initdeferred;
14310 fk_trigger->constrrel = NULL;
14311
14313 constraintOid, indexOid, InvalidOid,
14314 parentTrigOid, NULL, true, false);
14315
14316 /* Make changes-so-far visible */
14318
14319 return trigAddress.objectId;
14320}
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 14331 of file tablecmds.c.

14335{
14338
14339 /*
14340 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14341 * DELETE action on the referenced table.
14342 */
14344 fk_trigger->replace = false;
14345 fk_trigger->isconstraint = true;
14346 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14347 fk_trigger->relation = NULL;
14348 fk_trigger->args = NIL;
14349 fk_trigger->row = true;
14352 fk_trigger->columns = NIL;
14353 fk_trigger->whenClause = NULL;
14354 fk_trigger->transitionRels = NIL;
14355 fk_trigger->constrrel = NULL;
14356
14357 switch (fkconstraint->fk_del_action)
14358 {
14360 fk_trigger->deferrable = fkconstraint->deferrable;
14361 fk_trigger->initdeferred = fkconstraint->initdeferred;
14362 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_del");
14363 break;
14365 fk_trigger->deferrable = false;
14366 fk_trigger->initdeferred = false;
14367 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_del");
14368 break;
14370 fk_trigger->deferrable = false;
14371 fk_trigger->initdeferred = false;
14372 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_del");
14373 break;
14375 fk_trigger->deferrable = false;
14376 fk_trigger->initdeferred = false;
14377 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_del");
14378 break;
14380 fk_trigger->deferrable = false;
14381 fk_trigger->initdeferred = false;
14382 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_del");
14383 break;
14384 default:
14385 elog(ERROR, "unrecognized FK action type: %d",
14386 (int) fkconstraint->fk_del_action);
14387 break;
14388 }
14389
14391 constraintOid, indexOid, InvalidOid,
14392 parentDelTrigger, NULL, true, false);
14393 if (deleteTrigOid)
14394 *deleteTrigOid = trigAddress.objectId;
14395
14396 /* Make changes-so-far visible */
14398
14399 /*
14400 * Build and execute a CREATE CONSTRAINT TRIGGER statement for the ON
14401 * UPDATE action on the referenced table.
14402 */
14404 fk_trigger->replace = false;
14405 fk_trigger->isconstraint = true;
14406 fk_trigger->trigname = "RI_ConstraintTrigger_a";
14407 fk_trigger->relation = NULL;
14408 fk_trigger->args = NIL;
14409 fk_trigger->row = true;
14412 fk_trigger->columns = NIL;
14413 fk_trigger->whenClause = NULL;
14414 fk_trigger->transitionRels = NIL;
14415 fk_trigger->constrrel = NULL;
14416
14417 switch (fkconstraint->fk_upd_action)
14418 {
14420 fk_trigger->deferrable = fkconstraint->deferrable;
14421 fk_trigger->initdeferred = fkconstraint->initdeferred;
14422 fk_trigger->funcname = SystemFuncName("RI_FKey_noaction_upd");
14423 break;
14425 fk_trigger->deferrable = false;
14426 fk_trigger->initdeferred = false;
14427 fk_trigger->funcname = SystemFuncName("RI_FKey_restrict_upd");
14428 break;
14430 fk_trigger->deferrable = false;
14431 fk_trigger->initdeferred = false;
14432 fk_trigger->funcname = SystemFuncName("RI_FKey_cascade_upd");
14433 break;
14435 fk_trigger->deferrable = false;
14436 fk_trigger->initdeferred = false;
14437 fk_trigger->funcname = SystemFuncName("RI_FKey_setnull_upd");
14438 break;
14440 fk_trigger->deferrable = false;
14441 fk_trigger->initdeferred = false;
14442 fk_trigger->funcname = SystemFuncName("RI_FKey_setdefault_upd");
14443 break;
14444 default:
14445 elog(ERROR, "unrecognized FK action type: %d",
14446 (int) fkconstraint->fk_upd_action);
14447 break;
14448 }
14449
14451 constraintOid, indexOid, InvalidOid,
14452 parentUpdTrigger, NULL, true, false);
14453 if (updateTrigOid)
14454 *updateTrigOid = trigAddress.objectId;
14455}
#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 14466 of file tablecmds.c.

14471{
14473 constraintOid, indexOid,
14474 parentInsTrigger, true);
14476 constraintOid, indexOid,
14477 parentUpdTrigger, false);
14478}
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 17845 of file tablecmds.c.

17846{
17848 SysScanDesc scan;
17852
17853 /* Note: get RowExclusiveLock because we will write pg_inherits below. */
17855
17856 /*
17857 * Check for duplicates in the list of parents, and determine the highest
17858 * inhseqno already present; we'll use the next one for the new parent.
17859 * Also, if proposed child is a partition, it cannot already be
17860 * inheriting.
17861 *
17862 * Note: we do not reject the case where the child already inherits from
17863 * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
17864 */
17865 ScanKeyInit(&key,
17870 true, NULL, 1, &key);
17871
17872 /* inhseqno sequences start at 1 */
17873 inhseqno = 0;
17875 {
17877
17878 if (inh->inhparent == RelationGetRelid(parent_rel))
17879 ereport(ERROR,
17881 errmsg("relation \"%s\" would be inherited from more than once",
17883
17884 if (inh->inhseqno > inhseqno)
17885 inhseqno = inh->inhseqno;
17886 }
17887 systable_endscan(scan);
17888
17889 /* Match up the columns and bump attinhcount as needed */
17891
17892 /* Match up the constraints and bump coninhcount as needed */
17894
17895 /*
17896 * OK, it looks valid. Make the catalog entries that show inheritance.
17897 */
17900 inhseqno + 1,
17902 parent_rel->rd_rel->relkind ==
17904
17905 /* Now we're done with pg_inherits */
17907}
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:3629
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 22950 of file tablecmds.c.

22952{
22954 Oid newRelId;
22958 List *colList = NIL;
22959 Oid relamId;
22963
22964 /* If the existing rel is temp, it must belong to this session. */
22966 ereport(ERROR,
22968 errmsg("cannot create as partition of temporary relation of another session"));
22969
22970 /* Look up inheritance ancestors and generate the relation schema. */
22972
22973 /* Create a tuple descriptor from the relation schema. */
22975
22976 /* Look up the access method for the new relation. */
22978
22979 /* Look up the namespace in which we are supposed to create the relation. */
22980 namespaceId =
22983 ereport(ERROR,
22985 errmsg("relation \"%s\" already exists", newPartName->relname));
22986
22987 /*
22988 * We intended to create the partition with the same persistence as the
22989 * parent table, but we still need to recheck because that might be
22990 * affected by the search_path. If the parent is permanent, so must be
22991 * all of its partitions.
22992 */
22993 if (parent_relform->relpersistence != RELPERSISTENCE_TEMP &&
22994 newPartName->relpersistence == RELPERSISTENCE_TEMP)
22995 ereport(ERROR,
22997 errmsg("cannot create a temporary relation as partition of permanent relation \"%s\"",
22999
23000 /* Permanent rels cannot be partitions belonging to a temporary parent. */
23001 if (newPartName->relpersistence != RELPERSISTENCE_TEMP &&
23002 parent_relform->relpersistence == RELPERSISTENCE_TEMP)
23003 ereport(ERROR,
23005 errmsg("cannot create a permanent relation as partition of temporary relation \"%s\"",
23007
23008 /*
23009 * Select the tablespace for the new partition. Mirror the logic that
23010 * CREATE TABLE foo PARTITION OF ... uses in DefineRelation: take the
23011 * partitioned parent's explicit tablespace if it has one, otherwise take
23012 * default_tablespace into account, and finally use the database default.
23013 */
23014 tablespaceId = parent_relform->reltablespace;
23016 tablespaceId = GetDefaultTablespace(newPartName->relpersistence, false);
23017
23018 /* Check permissions except when using database's default */
23020 {
23022
23025 if (aclresult != ACLCHECK_OK)
23028 }
23029
23030 /* In all cases disallow placing user relations in pg_global */
23032 ereport(ERROR,
23034 errmsg("only shared relations can be placed in pg_global tablespace")));
23035
23036 /* Create the relation. */
23040 InvalidOid,
23041 InvalidOid,
23042 InvalidOid,
23043 ownerId,
23044 relamId,
23045 descriptor,
23046 NIL,
23048 newPartName->relpersistence,
23049 false,
23050 false,
23052 (Datum) 0,
23053 true,
23055 true,
23056 InvalidOid,
23057 NULL);
23058
23059 /*
23060 * We must bump the command counter to make the newly-created relation
23061 * tuple visible for opening.
23062 */
23064
23065 /*
23066 * Create a TOAST table if the table needs one. MERGE/SPLIT PARTITION
23067 * moves rows from existing partition(s) into new partition(s), which may
23068 * carry out-of-line varlena values that the new relation must be able to
23069 * store. Also, the new partition must be able to receive out-of-line
23070 * varlena values after the DDL operation is complete.
23071 */
23073
23074 /*
23075 * Open the new partition with no lock, because we already have an
23076 * AccessExclusiveLock placed there after creation.
23077 */
23079
23080 /* Find or create a work queue entry for the newly created table. */
23082
23083 /* Create constraints, default values, and generated values. */
23085
23086 /*
23087 * Need to call CommandCounterIncrement, so a fresh relcache entry has
23088 * newly installed constraint info.
23089 */
23091
23092 return newRel;
23093}
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 23712 of file tablecmds.c.

23713{
23715
23717 pc->partRel = partRel;
23718
23719 /*
23720 * Prepare a BulkInsertState for table_tuple_insert. The FSM is empty, so
23721 * don't bother using it.
23722 */
23723 pc->bistate = GetBulkInsertState();
23724
23725 /* Create a destination tuple slot for the new partition. */
23726 pc->dstslot = table_slot_create(pc->partRel, NULL);
23727
23728 return pc;
23729}
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 22743 of file tablecmds.c.

22745{
22747 TupleConstr *constr;
22748 AttrMap *attmap;
22750 int ccnum;
22751 List *constraints = NIL;
22753
22755 constr = tupleDesc->constr;
22756
22757 if (!constr)
22758 return;
22759
22760 /*
22761 * Construct a map from the parent relation's attnos to the child rel's.
22762 * This re-checks type match, etc, although it shouldn't be possible to
22763 * have a failure since both tables are locked.
22764 */
22766 tupleDesc,
22767 false);
22768
22769 /* Cycle for default values. */
22770 for (parent_attno = 1; parent_attno <= tupleDesc->natts; parent_attno++)
22771 {
22773 parent_attno - 1);
22774
22775 /* Ignore dropped columns in the parent. */
22776 if (attribute->attisdropped)
22777 continue;
22778
22779 /* Copy the default, if present, and it should be copied. */
22780 if (attribute->atthasdef)
22781 {
22783 bool found_whole_row;
22784 AttrNumber num;
22785 Node *def;
22787
22788 if (attribute->attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
22790 else
22791 {
22793 if (this_default == NULL)
22794 elog(ERROR, "default expression not found for attribute %d of relation \"%s\"",
22796 }
22797
22798 num = attmap->attnums[parent_attno - 1];
22799 def = map_variable_attnos(this_default, 1, 0, attmap, InvalidOid, &found_whole_row);
22800
22801 if (found_whole_row && attribute->attgenerated != '\0')
22802 elog(ERROR, "cannot convert whole-row table reference");
22803
22804 /* Add a pre-cooked default expression. */
22805 StoreAttrDefault(newRel, num, def, true);
22806
22807 /*
22808 * Stored generated column expressions in parent_rel might
22809 * reference the tableoid. newRel, parent_rel tableoid clear is
22810 * not the same. If so, these stored generated columns require
22811 * recomputation for newRel within MergePartitionsMoveRows.
22812 */
22813 if (attribute->attgenerated == ATTRIBUTE_GENERATED_STORED)
22814 {
22816 newval->attnum = num;
22817 newval->expr = expression_planner((Expr *) def);
22818 newval->is_generated = (attribute->attgenerated != '\0');
22819 tab->newvals = lappend(tab->newvals, newval);
22820 }
22821 }
22822 }
22823
22824 /* Cycle for CHECK constraints. */
22825 for (ccnum = 0; ccnum < constr->num_check; ccnum++)
22826 {
22827 char *ccname = constr->check[ccnum].ccname;
22828 char *ccbin = constr->check[ccnum].ccbin;
22829 bool ccenforced = constr->check[ccnum].ccenforced;
22830 bool ccnoinherit = constr->check[ccnum].ccnoinherit;
22831 bool ccvalid = constr->check[ccnum].ccvalid;
22833 bool found_whole_row;
22834 Constraint *con;
22835
22836 /*
22837 * The partitioned table can not have a NO INHERIT check constraint
22838 * (see StoreRelCheck function for details).
22839 */
22840 Assert(!ccnoinherit);
22841
22843 1, 0,
22844 attmap,
22845 InvalidOid, &found_whole_row);
22846
22847 /*
22848 * For the moment we have to reject whole-row variables (as for CREATE
22849 * TABLE LIKE and inheritances).
22850 */
22851 if (found_whole_row)
22852 elog(ERROR, "Constraint \"%s\" contains a whole-row reference to table \"%s\".",
22853 ccname,
22855
22856 con = makeNode(Constraint);
22857 con->contype = CONSTR_CHECK;
22858 con->conname = pstrdup(ccname);
22859 con->deferrable = false;
22860 con->initdeferred = false;
22861 con->is_enforced = ccenforced;
22862 con->skip_validation = !ccvalid;
22863 con->initially_valid = ccvalid;
22864 con->is_no_inherit = ccnoinherit;
22865 con->raw_expr = NULL;
22867 con->location = -1;
22868 constraints = lappend(constraints, con);
22869 }
22870
22871 /* Install all CHECK constraints. */
22873 false, true, true, NULL);
22874
22875 /* Make the additional catalog changes visible. */
22877
22878 /*
22879 * parent_rel check constraint expression may reference tableoid, so later
22880 * in MergePartitionsMoveRows, we need to evaluate the check constraint
22881 * again for the newRel. We can check whether the check constraint
22882 * contains a tableoid reference via pull_varattnos.
22883 */
22885 {
22886 if (!ccon->skip_validation)
22887 {
22888 Node *qual;
22889 Bitmapset *attnums = NULL;
22890
22891 Assert(ccon->contype == CONSTR_CHECK);
22893 pull_varattnos(qual, 1, &attnums);
22894
22895 /*
22896 * Add a check only if it contains a tableoid
22897 * (TableOidAttributeNumber).
22898 */
22900 attnums))
22901 {
22903
22905 newcon->name = ccon->name;
22906 newcon->contype = CONSTR_CHECK;
22907 newcon->qual = qual;
22908
22909 tab->constraints = lappend(tab->constraints, newcon);
22910 }
22911 }
22912 }
22913
22914 /* Don't need the cookedConstraints anymore. */
22916
22917 /* Reproduce not-null constraints. */
22918 if (constr->has_not_null)
22919 {
22920 List *nnconstraints;
22921
22922 /*
22923 * The "include_noinh" argument is false because a partitioned table
22924 * can't have NO INHERIT constraint.
22925 */
22927 false, false);
22928
22929 Assert(list_length(nnconstraints) > 0);
22930
22931 /*
22932 * We already set pg_attribute.attnotnull in createPartitionTable. No
22933 * need call set_attnotnull again.
22934 */
22935 AddRelationNewConstraints(newRel, NIL, nnconstraints, false, true, true, NULL);
22936 }
22937}
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 17914 of file tablecmds.c.

17915{
17917 bool isnull;
17918 Datum attr;
17919 Datum expr;
17920
17922 attr = heap_getattr(contup, Anum_pg_constraint_conbin, tupdesc, &isnull);
17923 if (isnull)
17924 elog(ERROR, "null conbin for constraint %u", con->oid);
17925
17926 expr = DirectFunctionCall2(pg_get_expr, attr,
17927 ObjectIdGetDatum(con->conrelid));
17928 return TextDatumGetCString(expr);
17929}
#define DirectFunctionCall2(func, arg1, arg2)
Definition fmgr.h:690
Datum pg_get_expr(PG_FUNCTION_ARGS)
Definition ruleutils.c:3033

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

Referenced by constraints_equivalent().

◆ DefineRelation()

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

Definition at line 817 of file tablecmds.c.

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

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

23736{
23737 ListCell *ltab;
23738
23740 FreeBulkInsertState(pc->bistate);
23741
23742 table_finish_bulk_insert(pc->partRel, ti_options);
23743
23744 /*
23745 * We don't need to process this pc->partRel so delete the ALTER TABLE
23746 * queue of it.
23747 */
23748 foreach(ltab, *wqueue)
23749 {
23751
23752 if (tab->relid == RelationGetRelid(pc->partRel))
23753 {
23755 break;
23756 }
23757 }
23758
23759 pfree(pc);
23760}
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 21611 of file tablecmds.c.

21613{
21615 List *fks;
21616 ListCell *cell;
21617 List *indexes;
21621 HeapTuple tuple,
21622 newtuple;
21624 List *fkoids = NIL;
21625
21626 if (concurrent)
21627 {
21628 /*
21629 * We can remove the pg_inherits row now. (In the non-concurrent case,
21630 * this was already done).
21631 */
21632 RemoveInheritance(partRel, rel, true);
21633 }
21634
21635 /* Drop any triggers that were cloned on creation/attach. */
21637
21638 /*
21639 * Detach any foreign keys that are inherited. This includes creating
21640 * additional action triggers.
21641 */
21643 if (fks != NIL)
21645
21646 /*
21647 * It's possible that the partition being detached has a foreign key that
21648 * references a partitioned table. When that happens, there are multiple
21649 * pg_constraint rows for the partition: one points to the partitioned
21650 * table itself, while the others point to each of its partitions. Only
21651 * the topmost one is to be considered here; the child constraints must be
21652 * left alone, because conceptually those aren't coming from our parent
21653 * partitioned table, but from this partition itself.
21654 *
21655 * We implement this by collecting all the constraint OIDs in a first scan
21656 * of the FK array, and skipping in the loop below those constraints whose
21657 * parents are listed here.
21658 */
21660 fkoids = lappend_oid(fkoids, fk->conoid);
21661
21662 foreach(cell, fks)
21663 {
21664 ForeignKeyCacheInfo *fk = lfirst(cell);
21667
21670 elog(ERROR, "cache lookup failed for constraint %u", fk->conoid);
21672
21673 /*
21674 * Consider only inherited foreign keys, and only if their parents
21675 * aren't in the list.
21676 */
21677 if (conform->contype != CONSTRAINT_FOREIGN ||
21678 !OidIsValid(conform->conparentid) ||
21679 list_member_oid(fkoids, conform->conparentid))
21680 {
21682 continue;
21683 }
21684
21685 /*
21686 * The constraint on this table must be marked no longer a child of
21687 * the parent's constraint, as do its check triggers.
21688 */
21690
21691 /*
21692 * Also, look up the partition's "check" triggers corresponding to the
21693 * ENFORCED constraint being detached and detach them from the parent
21694 * triggers. NOT ENFORCED constraints do not have these triggers;
21695 * therefore, this step is not needed.
21696 */
21697 if (fk->conenforced)
21698 {
21701
21703 fk->conoid, fk->confrelid, fk->conrelid,
21707 RelationGetRelid(partRel));
21710 RelationGetRelid(partRel));
21711 }
21712
21713 /*
21714 * Lastly, create the action triggers on the referenced table, using
21715 * addFkRecurseReferenced, which requires some elaborate setup (so put
21716 * it in a separate block). While at it, if the table is partitioned,
21717 * that function will recurse to create the pg_constraint rows and
21718 * action triggers for each partition.
21719 *
21720 * Note there's no need to do addFkConstraint() here, because the
21721 * pg_constraint row already exists.
21722 */
21723 {
21725 int numfks;
21731 int numfkdelsetcols;
21732 AttrNumber confdelsetcols[INDEX_MAX_KEYS];
21734
21736 &numfks,
21737 conkey,
21738 confkey,
21739 conpfeqop,
21740 conppeqop,
21741 conffeqop,
21743 confdelsetcols);
21744
21745 /* Create a synthetic node we'll use throughout */
21748 fkconstraint->conname = pstrdup(NameStr(conform->conname));
21749 fkconstraint->deferrable = conform->condeferrable;
21750 fkconstraint->initdeferred = conform->condeferred;
21751 fkconstraint->is_enforced = conform->conenforced;
21752 fkconstraint->skip_validation = true;
21753 fkconstraint->initially_valid = conform->convalidated;
21754 /* a few irrelevant fields omitted here */
21755 fkconstraint->pktable = NULL;
21756 fkconstraint->fk_attrs = NIL;
21757 fkconstraint->pk_attrs = NIL;
21758 fkconstraint->fk_matchtype = conform->confmatchtype;
21759 fkconstraint->fk_upd_action = conform->confupdtype;
21760 fkconstraint->fk_del_action = conform->confdeltype;
21761 fkconstraint->fk_del_set_cols = NIL;
21762 fkconstraint->old_conpfeqop = NIL;
21763 fkconstraint->old_pktable_oid = InvalidOid;
21764 fkconstraint->location = -1;
21765
21766 /* set up colnames, used to generate the constraint name */
21767 for (int i = 0; i < numfks; i++)
21768 {
21770
21771 att = TupleDescAttr(RelationGetDescr(partRel),
21772 conkey[i] - 1);
21773
21774 fkconstraint->fk_attrs = lappend(fkconstraint->fk_attrs,
21775 makeString(NameStr(att->attname)));
21776 }
21777
21779
21781 refdRel,
21782 conform->conindid,
21783 fk->conoid,
21784 numfks,
21785 confkey,
21786 conkey,
21787 conpfeqop,
21788 conppeqop,
21789 conffeqop,
21791 confdelsetcols,
21792 true,
21794 conform->conperiod);
21795 table_close(refdRel, NoLock); /* keep lock till end of xact */
21796 }
21797
21799 }
21801 if (trigrel)
21803
21804 /*
21805 * Any sub-constraints that are in the referenced-side of a larger
21806 * constraint have to be removed. This partition is no longer part of the
21807 * key space of the constraint.
21808 */
21809 foreach(cell, GetParentedForeignKeyRefs(partRel))
21810 {
21811 Oid constrOid = lfirst_oid(cell);
21812 ObjectAddress constraint;
21813
21816 constrOid,
21820
21822 performDeletion(&constraint, DROP_RESTRICT, 0);
21823 }
21824
21825 /* Now we can detach indexes */
21826 indexes = RelationGetIndexList(partRel);
21827 foreach(cell, indexes)
21828 {
21829 Oid idxid = lfirst_oid(cell);
21830 Oid parentidx;
21831 Relation idx;
21832 Oid constrOid;
21834
21835 if (!has_superclass(idxid))
21836 continue;
21837
21838 parentidx = get_partition_parent(idxid, false);
21839 Assert((IndexGetRelation(parentidx, false) == RelationGetRelid(rel)));
21840
21843
21844 /*
21845 * If there's a constraint associated with the index, detach it too.
21846 * Careful: it is possible for a constraint index in a partition to be
21847 * the child of a non-constraint index, so verify whether the parent
21848 * index does actually have a constraint.
21849 */
21851 idxid);
21853 parentidx);
21856
21858 }
21859
21860 /* Update pg_class tuple */
21864 if (!HeapTupleIsValid(tuple))
21865 elog(ERROR, "cache lookup failed for relation %u",
21866 RelationGetRelid(partRel));
21868
21869 /* Clear relpartbound and reset relispartition */
21870 memset(new_val, 0, sizeof(new_val));
21871 memset(new_null, false, sizeof(new_null));
21872 memset(new_repl, false, sizeof(new_repl));
21876 newtuple = heap_modify_tuple(tuple, RelationGetDescr(classRel),
21878
21879 ((Form_pg_class) GETSTRUCT(newtuple))->relispartition = false;
21880 CatalogTupleUpdate(classRel, &newtuple->t_self, newtuple);
21881 heap_freetuple(newtuple);
21883
21884 /*
21885 * Drop identity property from all identity columns of partition.
21886 */
21887 for (int attno = 0; attno < RelationGetNumberOfAttributes(partRel); attno++)
21888 {
21889 Form_pg_attribute attr = TupleDescAttr(partRel->rd_att, attno);
21890
21891 if (!attr->attisdropped && attr->attidentity)
21892 ATExecDropIdentity(partRel, NameStr(attr->attname), false,
21893 AccessExclusiveLock, true, true);
21894 }
21895
21897 {
21898 /*
21899 * If the relation being detached is the default partition itself,
21900 * remove it from the parent's pg_partitioned_table entry.
21901 *
21902 * If not, we must invalidate default partition's relcache entry, as
21903 * in StorePartitionBound: its partition constraint depends on every
21904 * other partition's partition constraint.
21905 */
21906 if (RelationGetRelid(partRel) == defaultPartOid)
21908 else
21910 }
21911
21912 /*
21913 * Invalidate the parent's relcache so that the partition is no longer
21914 * included in its partition descriptor.
21915 */
21917
21918 /*
21919 * If the partition we just detached is partitioned itself, invalidate
21920 * relcache for all descendent partitions too to ensure that their
21921 * rd_partcheck expression trees are rebuilt; must lock partitions before
21922 * doing so, using the same lockmode as what partRel has been locked with
21923 * by the caller.
21924 */
21925 if (partRel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
21926 {
21927 List *children;
21928
21929 children = find_all_inheritors(RelationGetRelid(partRel),
21931 foreach(cell, children)
21932 {
21934 }
21935 }
21936}
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 23256 of file tablecmds.c.

23257{
23258 /* Remove the pg_inherits row first. */
23260
23261 /*
23262 * Detaching the partition might involve TOAST table access, so ensure we
23263 * have a valid snapshot.
23264 */
23266
23267 /* Do the final part of detaching. */
23269
23271}

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

18632{
18634 SysScanDesc scan;
18635 ScanKeyData key[3];
18637
18639
18640 ScanKeyInit(&key[0],
18644 ScanKeyInit(&key[1],
18647 ObjectIdGetDatum(relid));
18648 ScanKeyInit(&key[2],
18651 Int32GetDatum(0));
18652
18654 NULL, 3, key);
18655
18657 {
18659
18660 if (dep->refclassid == refclassid &&
18661 dep->refobjid == refobjid &&
18662 dep->refobjsubid == 0 &&
18663 dep->deptype == deptype)
18665 }
18666
18667 systable_endscan(scan);
18669}

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

21980{
21982 SysScanDesc scan;
21985 ObjectAddresses *objects;
21986
21987 objects = new_object_addresses();
21988
21989 /*
21990 * Scan pg_trigger to search for all triggers on this rel.
21991 */
21996 true, NULL, 1, &skey);
21998 {
22001
22002 /* Ignore triggers that weren't cloned */
22003 if (!OidIsValid(pg_trigger->tgparentid))
22004 continue;
22005
22006 /*
22007 * Ignore internal triggers that are implementation objects of foreign
22008 * keys, because these will be detached when the foreign keys
22009 * themselves are.
22010 */
22011 if (OidIsValid(pg_trigger->tgconstrrelid))
22012 continue;
22013
22014 /*
22015 * This is ugly, but necessary: remove the dependency markings on the
22016 * trigger so that it can be removed.
22017 */
22024
22025 /* remember this trigger to remove it below */
22027 add_exact_object_address(&trig, objects);
22028 }
22029
22030 /* make the dependency removal visible to the deletion below */
22033
22034 /* done */
22035 free_object_addresses(objects);
22036 systable_endscan(scan);
22038}

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

14554{
14558 List *children;
14559 bool is_no_inherit_constraint = false;
14560 char *constrName;
14561 char *colname = NULL;
14562
14563 /* Guard against stack overflow due to overly deep inheritance tree. */
14565
14566 /* At top level, permission check was done in ATPrepCmd, else do it */
14567 if (recursing)
14570
14572
14574 constrName = NameStr(con->conname);
14575
14576 /* Don't allow drop of inherited constraints */
14577 if (con->coninhcount > 0 && !recursing)
14578 ereport(ERROR,
14580 errmsg("cannot drop inherited constraint \"%s\" of relation \"%s\"",
14582
14583 /*
14584 * Reset pg_constraint.attnotnull, if this is a not-null constraint.
14585 *
14586 * While doing that, we're in a good position to disallow dropping a not-
14587 * null constraint underneath a primary key, a replica identity index, or
14588 * a generated identity column.
14589 */
14590 if (con->contype == CONSTRAINT_NOTNULL)
14591 {
14598
14599 /* save column name for recursion step */
14600 colname = get_attname(RelationGetRelid(rel), attnum, false);
14601
14602 /*
14603 * Disallow if it's in the primary key. For partitioned tables we
14604 * cannot rely solely on RelationGetIndexAttrBitmap, because it'll
14605 * return NULL if the primary key is invalid; but we still need to
14606 * protect not-null constraints under such a constraint, so check the
14607 * slow way.
14608 */
14610
14611 if (pkattrs == NULL &&
14612 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14613 {
14615
14616 if (OidIsValid(pkindex))
14617 {
14619
14620 pkattrs = NULL;
14621 for (int i = 0; i < pk->rd_index->indnkeyatts; i++)
14622 pkattrs = bms_add_member(pkattrs, pk->rd_index->indkey.values[i]);
14623
14625 }
14626 }
14627
14628 if (pkattrs &&
14630 ereport(ERROR,
14632 errmsg("column \"%s\" is in a primary key",
14633 get_attname(RelationGetRelid(rel), attnum, false)));
14634
14635 /* Disallow if it's in the replica identity */
14638 ereport(ERROR,
14640 errmsg("column \"%s\" is in index used as replica identity",
14641 get_attname(RelationGetRelid(rel), attnum, false)));
14642
14643 /* Disallow if it's a GENERATED AS IDENTITY column */
14646 elog(ERROR, "cache lookup failed for attribute %d of relation %u",
14647 attnum, RelationGetRelid(rel));
14649 if (attForm->attidentity != '\0')
14650 ereport(ERROR,
14652 errmsg("column \"%s\" of relation \"%s\" is an identity column",
14654 false),
14656
14657 /* All good -- reset attnotnull if needed */
14658 if (attForm->attnotnull)
14659 {
14660 attForm->attnotnull = false;
14662 }
14663
14665 }
14666
14667 is_no_inherit_constraint = con->connoinherit;
14668
14669 /*
14670 * If it's a foreign-key constraint, we'd better lock the referenced table
14671 * and check that that's not in use, just as we've already done for the
14672 * constrained table (else we might, eg, be dropping a trigger that has
14673 * unfired events). But we can/must skip that in the self-referential
14674 * case.
14675 */
14676 if (con->contype == CONSTRAINT_FOREIGN &&
14677 con->confrelid != RelationGetRelid(rel))
14678 {
14679 Relation frel;
14680
14681 /* Must match lock taken by RemoveTriggerById: */
14682 frel = table_open(con->confrelid, AccessExclusiveLock);
14685 }
14686
14687 /*
14688 * Perform the actual constraint deletion
14689 */
14691 performDeletion(&conobj, behavior, 0);
14692
14693 /*
14694 * For partitioned tables, non-CHECK, non-NOT-NULL inherited constraints
14695 * are dropped via the dependency mechanism, so we're done here.
14696 */
14697 if (con->contype != CONSTRAINT_CHECK &&
14698 con->contype != CONSTRAINT_NOTNULL &&
14699 rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
14700 {
14702 return conobj;
14703 }
14704
14705 /*
14706 * Propagate to children as appropriate. Unlike most other ALTER
14707 * routines, we have to do this one level of recursion at a time; we can't
14708 * use find_all_inheritors to do it in one pass.
14709 */
14711 children = find_inheritance_children(RelationGetRelid(rel), lockmode);
14712 else
14713 children = NIL;
14714
14715 foreach_oid(childrelid, children)
14716 {
14718 HeapTuple tuple;
14720
14721 /* find_inheritance_children already got lock */
14724
14725 /*
14726 * We search for not-null constraints by column name, and others by
14727 * constraint name.
14728 */
14729 if (con->contype == CONSTRAINT_NOTNULL)
14730 {
14731 tuple = findNotNullConstraint(childrelid, colname);
14732 if (!HeapTupleIsValid(tuple))
14733 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation %u",
14734 colname, RelationGetRelid(childrel));
14735 }
14736 else
14737 {
14738 SysScanDesc scan;
14739 ScanKeyData skey[3];
14740
14741 ScanKeyInit(&skey[0],
14745 ScanKeyInit(&skey[1],
14749 ScanKeyInit(&skey[2],
14754 true, NULL, 3, skey);
14755 /* There can only be one, so no need to loop */
14756 tuple = systable_getnext(scan);
14757 if (!HeapTupleIsValid(tuple))
14758 ereport(ERROR,
14760 errmsg("constraint \"%s\" of relation \"%s\" does not exist",
14761 constrName,
14763 tuple = heap_copytuple(tuple);
14764 systable_endscan(scan);
14765 }
14766
14768
14769 /* Right now only CHECK and not-null constraints can be inherited */
14770 if (childcon->contype != CONSTRAINT_CHECK &&
14771 childcon->contype != CONSTRAINT_NOTNULL)
14772 elog(ERROR, "inherited constraint is not a CHECK or not-null constraint");
14773
14774 if (childcon->coninhcount <= 0) /* shouldn't happen */
14775 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
14776 childrelid, NameStr(childcon->conname));
14777
14778 if (recurse)
14779 {
14780 /*
14781 * If the child constraint has other definition sources, just
14782 * decrement its inheritance count; if not, recurse to delete it.
14783 */
14784 if (childcon->coninhcount == 1 && !childcon->conislocal)
14785 {
14786 /* Time to delete this child constraint, too */
14787 dropconstraint_internal(childrel, tuple, behavior,
14788 recurse, true, missing_ok,
14789 lockmode);
14790 }
14791 else
14792 {
14793 /* Child constraint must survive my deletion */
14794 childcon->coninhcount--;
14795 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14796
14797 /* Make update visible */
14799 }
14800 }
14801 else
14802 {
14803 /*
14804 * If we were told to drop ONLY in this table (no recursion) and
14805 * there are no further parents for this constraint, we need to
14806 * mark the inheritors' constraints as locally defined rather than
14807 * inherited.
14808 */
14809 childcon->coninhcount--;
14810 if (childcon->coninhcount == 0)
14811 childcon->conislocal = true;
14812
14813 CatalogTupleUpdate(conrel, &tuple->t_self, tuple);
14814
14815 /* Make update visible */
14817 }
14818
14819 heap_freetuple(tuple);
14820
14822 }
14823
14825
14826 return conobj;
14827}
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition bitmapset.c:799
Oid RelationGetPrimaryKeyIndex(Relation relation, bool deferrable_ok)
Definition relcache.c:5057
Bitmapset * RelationGetIndexAttrBitmap(Relation relation, IndexAttrBitmapKind attrKind)
Definition relcache.c:5313
@ 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 1521 of file tablecmds.c.

1522{
1523 const struct dropmsgstrings *rentry;
1524
1525 if (rel->schemaname != NULL &&
1527 {
1528 if (!missing_ok)
1529 {
1530 ereport(ERROR,
1532 errmsg("schema \"%s\" does not exist", rel->schemaname)));
1533 }
1534 else
1535 {
1537 (errmsg("schema \"%s\" does not exist, skipping",
1538 rel->schemaname)));
1539 }
1540 return;
1541 }
1542
1543 for (rentry = dropmsgstringarray; rentry->kind != '\0'; rentry++)
1544 {
1545 if (rentry->kind == rightkind)
1546 {
1547 if (!missing_ok)
1548 {
1549 ereport(ERROR,
1550 (errcode(rentry->nonexistent_code),
1551 errmsg(rentry->nonexistent_msg, rel->relname)));
1552 }
1553 else
1554 {
1555 ereport(NOTICE, (errmsg(rentry->skipping_msg, rel->relname)));
1556 break;
1557 }
1558 }
1559 }
1560
1561 Assert(rentry->kind != '\0'); /* Should be impossible */
1562}
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 1569 of file tablecmds.c.

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

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

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

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

23279{
23280 ListCell *la,
23281 *lb;
23282
23283 if (list_length(a) != list_length(b))
23284 return false;
23285
23286 forboth(la, a, lb, b)
23287 {
23288 if (lfirst_oid(la) != lfirst_oid(lb))
23289 return false;
23290 }
23291 return true;
23292}

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

22649{
22650 econtext->ecxt_scantuple = insertslot;
22651
22653 {
22654 if (!ex->is_generated)
22655 continue;
22656
22657 insertslot->tts_values[ex->attnum - 1]
22658 = ExecEvalExpr(ex->exprstate,
22659 econtext,
22660 &insertslot->tts_isnull[ex->attnum - 1]);
22661 }
22662
22664 {
22665 switch (con->contype)
22666 {
22667 case CONSTR_CHECK:
22668 if (!ExecCheck(con->qualstate, econtext))
22669 ereport(ERROR,
22671 errmsg("check constraint \"%s\" of relation \"%s\" is violated by some row",
22673 errtableconstraint(newPartRel, con->name));
22674 break;
22675 case CONSTR_NOTNULL:
22676 case CONSTR_FOREIGN:
22677 /* Nothing to do here */
22678 break;
22679 default:
22680 elog(ERROR, "unrecognized constraint type: %d",
22681 (int) con->contype);
22682 }
22683 }
22684}
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 1923 of file tablecmds.c.

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

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

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

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

Referenced by apply_handle_truncate(), and ExecuteTruncate().

◆ find_composite_type_dependencies()

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

Definition at line 7033 of file tablecmds.c.

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

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

7192{
7194 ScanKeyData key[1];
7195 TableScanDesc scan;
7196 HeapTuple tuple;
7197 List *result = NIL;
7198
7200
7201 ScanKeyInit(&key[0],
7204 ObjectIdGetDatum(typeOid));
7205
7206 scan = table_beginscan_catalog(classRel, 1, key);
7207
7208 while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
7209 {
7211
7212 if (behavior == DROP_RESTRICT)
7213 ereport(ERROR,
7215 errmsg("cannot alter type \"%s\" because it is the type of a typed table",
7216 typeName),
7217 errhint("Use ALTER ... CASCADE to alter the typed tables too.")));
7218 else
7220 }
7221
7222 table_endscan(scan);
7224
7225 return result;
7226}

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

3675{
3676 ListCell *lc;
3677 int i = 1;
3678
3679 foreach(lc, columns)
3680 {
3681 if (strcmp(attributeName, lfirst_node(ColumnDef, lc)->colname) == 0)
3682 return i;
3683
3684 i++;
3685 }
3686 return 0;
3687}

References fb(), i, and lfirst_node.

Referenced by MergeAttributes().

◆ findFkeyCast()

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

Definition at line 14110 of file tablecmds.c.

14111{
14112 CoercionPathType ret;
14113
14115 {
14117 *funcid = InvalidOid;
14118 }
14119 else
14120 {
14122 COERCION_IMPLICIT, funcid);
14123 if (ret == COERCION_PATH_NONE)
14124 /* A previously-relied-upon cast is now gone. */
14125 elog(ERROR, "could not find cast from %u to %u",
14127 }
14128
14129 return ret;
14130}
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 23481 of file tablecmds.c.

23482{
23484 {
23485 list_free(entry->extensionOids);
23486 pfree(entry);
23487 }
23489}

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

22530{
22531 char cmethod;
22532
22533 if (compression == NULL || strcmp(compression, "default") == 0)
22535
22536 /*
22537 * To specify a nondefault method, the column data type must be toastable.
22538 * Note this says nothing about whether the column's attstorage setting
22539 * permits compression; we intentionally allow attstorage and
22540 * attcompression to be independent. But with a non-toastable type,
22541 * attstorage could not be set to a value that would permit compression.
22542 *
22543 * We don't actually need to enforce this, since nothing bad would happen
22544 * if attcompression were non-default; it would never be consulted. But
22545 * it seems more user-friendly to complain about a certainly-useless
22546 * attempt to set the property.
22547 */
22549 ereport(ERROR,
22551 errmsg("column data type %s does not support compression",
22553
22554 cmethod = CompressionNameToMethod(compression);
22556 ereport(ERROR,
22558 errmsg("invalid compression method \"%s\"", compression)));
22559
22560 return cmethod;
22561}
#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 22690 of file tablecmds.c.

22691{
22694 List *colList = NIL;
22695
22697
22698 for (parent_attno = 1; parent_attno <= modelDesc->natts;
22699 parent_attno++)
22700 {
22702 parent_attno - 1);
22703 ColumnDef *def;
22704
22705 /* Ignore dropped columns in the parent. */
22706 if (attribute->attisdropped)
22707 continue;
22708
22709 def = makeColumnDef(NameStr(attribute->attname), attribute->atttypid,
22710 attribute->atttypmod, attribute->attcollation);
22711
22712 def->is_not_null = attribute->attnotnull;
22713
22714 /* Copy identity. */
22715 def->identity = attribute->attidentity;
22716
22717 /* Copy attgenerated. */
22718 def->generated = attribute->attgenerated;
22719
22720 def->storage = attribute->attstorage;
22721
22722 /* Likewise, copy compression. */
22723 if (CompressionMethodIsValid(attribute->attcompression))
22724 def->compression =
22725 pstrdup(GetCompressionMethodName(attribute->attcompression));
22726 else
22727 def->compression = NULL;
22728
22729 /* Add to column list. */
22730 colList = lappend(colList, def);
22731 }
22732
22733 return colList;
22734}
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 22567 of file tablecmds.c.

22568{
22569 char cstorage = 0;
22570
22571 if (pg_strcasecmp(storagemode, "plain") == 0)
22573 else if (pg_strcasecmp(storagemode, "external") == 0)
22575 else if (pg_strcasecmp(storagemode, "extended") == 0)
22577 else if (pg_strcasecmp(storagemode, "main") == 0)
22579 else if (pg_strcasecmp(storagemode, "default") == 0)
22581 else
22582 ereport(ERROR,
22584 errmsg("invalid storage type \"%s\"",
22585 storagemode)));
22586
22587 /*
22588 * safety check: do not allow toasted storage modes unless column datatype
22589 * is TOAST-aware.
22590 */
22592 ereport(ERROR,
22594 errmsg("column data type %s can only have storage PLAIN",
22596
22597 return cstorage;
22598}
char get_typstorage(Oid typid)
Definition lsyscache.c:2727
int pg_strcasecmp(const char *s1, const char *s2)

References ereport, errcode(), errmsg, ERROR, fb(), format_type_be(), get_typstorage(), pg_strcasecmp(), and TypeIsToastable.

Referenced by ATExecSetStorage(), and BuildDescForRelation().

◆ GetForeignKeyActionTriggers()

static void GetForeignKeyActionTriggers ( Relation  trigrel,
Oid  conoid,
Oid  confrelid,
Oid  conrelid,
Oid deleteTriggerOid,
Oid updateTriggerOid 
)
static

Definition at line 12156 of file tablecmds.c.

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

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

22428{
22430 HeapTuple tuple;
22431 SysScanDesc scan;
22432 ScanKeyData key[2];
22433 List *constraints = NIL;
22434
22435 /*
22436 * If no indexes, or no columns are referenceable by FKs, we can avoid the
22437 * scan.
22438 */
22442 return NIL;
22443
22444 /* Search for constraints referencing this table */
22446 ScanKeyInit(&key[0],
22449 ScanKeyInit(&key[1],
22452
22453 /* XXX This is a seqscan, as we don't have a usable index */
22454 scan = systable_beginscan(pg_constraint, InvalidOid, true, NULL, 2, key);
22455 while ((tuple = systable_getnext(scan)) != NULL)
22456 {
22458
22459 /*
22460 * We only need to process constraints that are part of larger ones.
22461 */
22462 if (!OidIsValid(constrForm->conparentid))
22463 continue;
22464
22465 constraints = lappend_oid(constraints, constrForm->oid);
22466 }
22467
22468 systable_endscan(scan);
22470
22471 return constraints;
22472}
#define bms_is_empty(a)
Definition bitmapset.h:118
@ INDEX_ATTR_BITMAP_KEY
Definition relcache.h:70

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

Referenced by ATDetachCheckNoForeignKeyRefs(), and DetachPartitionFinalize().

◆ index_copy_data()

static void index_copy_data ( Relation  rel,
RelFileLocator  newrlocator 
)
static

Definition at line 17622 of file tablecmds.c.

17623{
17625
17626 /*
17627 * Since we copy the file directly without looking at the shared buffers,
17628 * we'd better first flush out any pages of the source relation that are
17629 * in shared buffers. We assume no new changes will be made while we are
17630 * holding exclusive lock on the rel.
17631 */
17633
17634 /*
17635 * Create and copy all forks of the relation, and schedule unlinking of
17636 * old physical files.
17637 *
17638 * NOTE: any conflict in relfilenumber value will be caught in
17639 * RelationCreateStorage().
17640 */
17641 dstrel = RelationCreateStorage(newrlocator, rel->rd_rel->relpersistence, true);
17642
17643 /* copy main fork */
17645 rel->rd_rel->relpersistence);
17646
17647 /* copy those extra forks that exist */
17648 for (ForkNumber forkNum = MAIN_FORKNUM + 1;
17649 forkNum <= MAX_FORKNUM; forkNum++)
17650 {
17651 if (smgrexists(RelationGetSmgr(rel), forkNum))
17652 {
17653 smgrcreate(dstrel, forkNum, false);
17654
17655 /*
17656 * WAL log creation if the relation is persistent, or this is the
17657 * init fork of an unlogged relation.
17658 */
17659 if (RelationIsPermanent(rel) ||
17660 (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED &&
17661 forkNum == INIT_FORKNUM))
17662 log_smgrcreate(&newrlocator, forkNum);
17664 rel->rd_rel->relpersistence);
17665 }
17666 }
17667
17668 /* drop old relation, and close new one */
17671}
void FlushRelationBuffers(Relation rel)
Definition bufmgr.c:5171
static SMgrRelation RelationGetSmgr(Relation rel)
Definition rel.h:578
ForkNumber
Definition relpath.h:56
@ MAIN_FORKNUM
Definition relpath.h:58
@ INIT_FORKNUM
Definition relpath.h:61
#define MAX_FORKNUM
Definition relpath.h:70
void smgrcreate(SMgrRelation reln, ForkNumber forknum, bool isRedo)
Definition smgr.c:481
void smgrclose(SMgrRelation reln)
Definition smgr.c:374
bool smgrexists(SMgrRelation reln, ForkNumber forknum)
Definition smgr.c:462
void RelationCopyStorage(SMgrRelation src, SMgrRelation dst, ForkNumber forkNum, char relpersistence)
Definition storage.c:478
SMgrRelation RelationCreateStorage(RelFileLocator rlocator, char relpersistence, bool register_delete)
Definition storage.c:122
void log_smgrcreate(const RelFileLocator *rlocator, ForkNumber forkNum)
Definition storage.c:187
void RelationDropStorage(Relation rel)
Definition storage.c:207

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

Referenced by ATExecSetTableSpace().

◆ MarkInheritDetached()

static void MarkInheritDetached ( Relation  child_rel,
Relation  parent_rel 
)
static

Definition at line 18333 of file tablecmds.c.

18334{
18336 SysScanDesc scan;
18339 bool found = false;
18340
18341 Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
18342
18343 /*
18344 * Find pg_inherits entries by inhparent. (We need to scan them all in
18345 * order to verify that no other partition is pending detach.)
18346 */
18348 ScanKeyInit(&key,
18353 true, NULL, 1, &key);
18354
18356 {
18358
18360 if (inhForm->inhdetachpending)
18361 ereport(ERROR,
18363 errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
18364 get_rel_name(inhForm->inhrelid),
18365 get_namespace_name(parent_rel->rd_rel->relnamespace),
18367 errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the pending detach operation."));
18368
18369 if (inhForm->inhrelid == RelationGetRelid(child_rel))
18370 {
18372
18375
18377 &inheritsTuple->t_self,
18378 newtup);
18379 found = true;
18381 /* keep looking, to ensure we catch others pending detach */
18382 }
18383 }
18384
18385 /* Done */
18386 systable_endscan(scan);
18388
18389 if (!found)
18390 ereport(ERROR,
18392 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18395}
#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 2611 of file tablecmds.c.

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

References aclcheck_error(), ACLCHECK_NOT_OWNER, Assert, bms_add_member(), bms_is_member(), ConstrCheck::ccname, TupleConstr::check, CheckTableNotInUse(), ColumnDef::colname, CompressionMethodIsValid, CONSTR_NOTNULL, ColumnDef::cooked_default, elog, equal(), ereport, errcode(), errdetail(), errhint(), errmsg, ERROR, fb(), findAttrByName(), forboth, foreach_ptr, free_attrmap(), ColumnDef::generated, get_relkind_objtype(), GetCompressionMethodName(), GetUserId(), i, InvalidOid, lappend(), lfirst, lfirst_node, lfirst_oid, list_delete_nth_cell(), list_length(), list_nth_node, make_attrmap(), makeColumnDef(), map_variable_attnos(), MaxHeapAttributeNumber, MergeCheckConstraint(), MergeChildAttribute(), MergeInheritedAttribute(), name, NameStr, NIL, NoLock, TupleConstr::num_check, object_ownercheck(), pstrdup(), ColumnDef::raw_default, RelationData::rd_rel, RELATION_IS_OTHER_TEMP, RelationGetDescr, RelationGetNotNullConstraints(), RelationGetRelationName, RelationGetRelid, stringToNode(), table_close(), table_open(), TupleDescAttr(), and TupleDescGetDefault().

Referenced by DefineRelation().

◆ MergeAttributesIntoExisting()

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

Definition at line 17971 of file tablecmds.c.

17972{
17975
17978
17980 {
17982 char *parent_attname = NameStr(parent_att->attname);
17983 HeapTuple tuple;
17984
17985 /* Ignore dropped columns in the parent. */
17986 if (parent_att->attisdropped)
17987 continue;
17988
17989 /* Find same column in child (matching on column name). */
17991 if (HeapTupleIsValid(tuple))
17992 {
17994
17995 if (parent_att->atttypid != child_att->atttypid ||
17996 parent_att->atttypmod != child_att->atttypmod)
17997 ereport(ERROR,
17999 errmsg("child table \"%s\" has different type for column \"%s\"",
18001
18002 if (parent_att->attcollation != child_att->attcollation)
18003 ereport(ERROR,
18005 errmsg("child table \"%s\" has different collation for column \"%s\"",
18007
18008 /*
18009 * If the parent has a not-null constraint that's not NO INHERIT,
18010 * make sure the child has one too.
18011 *
18012 * Other constraints are checked elsewhere.
18013 */
18014 if (parent_att->attnotnull && !child_att->attnotnull)
18015 {
18017
18019 parent_att->attnum);
18020 if (HeapTupleIsValid(contup) &&
18022 ereport(ERROR,
18024 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
18026 }
18027
18028 /*
18029 * Child column must be generated if and only if parent column is.
18030 */
18031 if (parent_att->attgenerated && !child_att->attgenerated)
18032 ereport(ERROR,
18034 errmsg("column \"%s\" in child table must be a generated column", parent_attname)));
18035 if (child_att->attgenerated && !parent_att->attgenerated)
18036 ereport(ERROR,
18038 errmsg("column \"%s\" in child table must not be a generated column", parent_attname)));
18039
18040 if (parent_att->attgenerated && child_att->attgenerated && child_att->attgenerated != parent_att->attgenerated)
18041 ereport(ERROR,
18043 errmsg("column \"%s\" inherits from generated column of different kind", parent_attname),
18044 errdetail("Parent column is %s, child column is %s.",
18045 parent_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL",
18046 child_att->attgenerated == ATTRIBUTE_GENERATED_STORED ? "STORED" : "VIRTUAL")));
18047
18048 /*
18049 * Regular inheritance children are independent enough not to
18050 * inherit identity columns. But partitions are integral part of
18051 * a partitioned table and inherit identity column.
18052 */
18053 if (ispartition)
18054 child_att->attidentity = parent_att->attidentity;
18055
18056 /*
18057 * OK, bump the child column's inheritance count. (If we fail
18058 * later on, this change will just roll back.)
18059 */
18060 if (pg_add_s16_overflow(child_att->attinhcount, 1,
18061 &child_att->attinhcount))
18062 ereport(ERROR,
18064 errmsg("too many inheritance parents"));
18065
18066 /*
18067 * In case of partitions, we must enforce that value of attislocal
18068 * is same in all partitions. (Note: there are only inherited
18069 * attributes in partitions)
18070 */
18071 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18072 {
18073 Assert(child_att->attinhcount == 1);
18074 child_att->attislocal = false;
18075 }
18076
18077 CatalogTupleUpdate(attrrel, &tuple->t_self, tuple);
18078 heap_freetuple(tuple);
18079 }
18080 else
18081 {
18082 ereport(ERROR,
18084 errmsg("child table is missing column \"%s\"", parent_attname)));
18085 }
18086 }
18087
18089}

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

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

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

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

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

18110{
18115 Oid parent_relid = RelationGetRelid(parent_rel);
18116 AttrMap *attmap;
18117
18119
18120 /* Outer loop scans through the parent's constraint definitions */
18124 ObjectIdGetDatum(parent_relid));
18126 true, NULL, 1, &parent_key);
18127
18130 true);
18131
18133 {
18139 bool found = false;
18140
18141 if (parent_con->contype != CONSTRAINT_CHECK &&
18142 parent_con->contype != CONSTRAINT_NOTNULL)
18143 continue;
18144
18145 /* if the parent's constraint is marked NO INHERIT, it's not inherited */
18146 if (parent_con->connoinherit)
18147 continue;
18148
18149 if (parent_con->contype == CONSTRAINT_NOTNULL)
18151 else
18153
18154 /* Search for a child constraint matching this one */
18160 true, NULL, 1, &child_key);
18161
18163 {
18166
18167 if (child_con->contype != parent_con->contype)
18168 continue;
18169
18170 /*
18171 * CHECK constraint are matched by constraint name, NOT NULL ones
18172 * by attribute number.
18173 */
18174 if (child_con->contype == CONSTRAINT_CHECK)
18175 {
18176 if (strcmp(NameStr(parent_con->conname),
18177 NameStr(child_con->conname)) != 0)
18178 continue;
18179 }
18180 else if (child_con->contype == CONSTRAINT_NOTNULL)
18181 {
18185
18188 if (parent_attno != attmap->attnums[child_attno - 1])
18189 continue;
18190
18192 /* there shouldn't be constraints on dropped columns */
18193 if (parent_attr->attisdropped || child_attr->attisdropped)
18194 elog(ERROR, "found not-null constraint on dropped columns");
18195 }
18196
18197 if (child_con->contype == CONSTRAINT_CHECK &&
18199 ereport(ERROR,
18201 errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
18203
18204 /*
18205 * If the child constraint is "no inherit" then cannot merge
18206 */
18207 if (child_con->connoinherit)
18208 ereport(ERROR,
18210 errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
18212
18213 /*
18214 * If the child constraint is "not valid" then cannot merge with a
18215 * valid parent constraint
18216 */
18217 if (parent_con->convalidated && child_con->conenforced &&
18218 !child_con->convalidated)
18219 ereport(ERROR,
18221 errmsg("constraint \"%s\" conflicts with NOT VALID constraint on child table \"%s\"",
18223
18224 /*
18225 * A NOT ENFORCED child constraint cannot be merged with an
18226 * ENFORCED parent constraint. However, the reverse is allowed,
18227 * where the child constraint is ENFORCED.
18228 */
18229 if (parent_con->conenforced && !child_con->conenforced)
18230 ereport(ERROR,
18232 errmsg("constraint \"%s\" conflicts with NOT ENFORCED constraint on child table \"%s\"",
18234
18235 /*
18236 * OK, bump the child constraint's inheritance count. (If we fail
18237 * later on, this change will just roll back.)
18238 */
18241
18242 if (pg_add_s16_overflow(child_con->coninhcount, 1,
18243 &child_con->coninhcount))
18244 ereport(ERROR,
18246 errmsg("too many inheritance parents"));
18247
18248 /*
18249 * In case of partitions, an inherited constraint must be
18250 * inherited only once since it cannot have multiple parents and
18251 * it is never considered local.
18252 */
18253 if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
18254 {
18255 Assert(child_con->coninhcount == 1);
18256 child_con->conislocal = false;
18257 }
18258
18261
18262 found = true;
18263 break;
18264 }
18265
18267
18268 if (!found)
18269 {
18270 if (parent_con->contype == CONSTRAINT_NOTNULL)
18271 ereport(ERROR,
18273 errmsg("column \"%s\" in child table \"%s\" must be marked NOT NULL",
18274 get_attname(parent_relid,
18276 false),
18278
18279 ereport(ERROR,
18281 errmsg("child table is missing constraint \"%s\"",
18282 NameStr(parent_con->conname))));
18283 }
18284 }
18285
18288}

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

3483{
3484 char *attributeName = newdef->colname;
3487 newtypeid;
3489 newtypmod;
3491 newcollid;
3492
3494 (errmsg("merging multiple inherited definitions of column \"%s\"",
3495 attributeName)));
3497
3498 /*
3499 * Must have the same type and typmod
3500 */
3504 ereport(ERROR,
3506 errmsg("inherited column \"%s\" has a type conflict",
3508 errdetail("%s versus %s",
3511
3512 /*
3513 * Must have the same collation
3514 */
3517 if (prevcollid != newcollid)
3518 ereport(ERROR,
3520 errmsg("inherited column \"%s\" has a collation conflict",
3522 errdetail("\"%s\" versus \"%s\"",
3525
3526 /*
3527 * Copy/check storage parameter
3528 */
3529 if (prevdef->storage == 0)
3530 prevdef->storage = newdef->storage;
3531 else if (prevdef->storage != newdef->storage)
3532 ereport(ERROR,
3534 errmsg("inherited column \"%s\" has a storage parameter conflict",
3536 errdetail("%s versus %s",
3537 storage_name(prevdef->storage),
3538 storage_name(newdef->storage))));
3539
3540 /*
3541 * Copy/check compression parameter
3542 */
3543 if (prevdef->compression == NULL)
3544 prevdef->compression = newdef->compression;
3545 else if (newdef->compression != NULL)
3546 {
3547 if (strcmp(prevdef->compression, newdef->compression) != 0)
3548 ereport(ERROR,
3550 errmsg("column \"%s\" has a compression method conflict",
3552 errdetail("%s versus %s",
3553 prevdef->compression, newdef->compression)));
3554 }
3555
3556 /*
3557 * Check for GENERATED conflicts
3558 */
3559 if (prevdef->generated != newdef->generated)
3560 ereport(ERROR,
3562 errmsg("inherited column \"%s\" has a generation conflict",
3563 attributeName)));
3564
3565 /*
3566 * Default and other constraints are handled by the caller.
3567 */
3568
3569 if (pg_add_s16_overflow(prevdef->inhcount, 1,
3570 &prevdef->inhcount))
3571 ereport(ERROR,
3573 errmsg("too many inheritance parents"));
3574
3575 return prevdef;
3576}

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

23102{
23103 CommandId mycid;
23104 EState *estate;
23105 AlteredTableInfo *tab;
23106 ListCell *ltab;
23107
23108 /* The FSM is empty, so don't bother using it. */
23109 uint32 ti_options = TABLE_INSERT_SKIP_FSM;
23110 BulkInsertState bistate; /* state of bulk inserts for partition */
23111 TupleTableSlot *dstslot;
23112
23113 /* Find the work queue entry for the new partition table: newPartRel. */
23115
23116 /* Generate the constraint and default execution states. */
23117 estate = CreateExecutorState();
23118
23120
23121 mycid = GetCurrentCommandId(true);
23122
23123 /* Prepare a BulkInsertState for table_tuple_insert. */
23124 bistate = GetBulkInsertState();
23125
23126 /* Create the necessary tuple slot. */
23127 dstslot = table_slot_create(newPartRel, NULL);
23128
23130 {
23131 ExprContext *econtext;
23134 TableScanDesc scan;
23136 Snapshot snapshot;
23138
23139 econtext = GetPerTupleExprContext(estate);
23140
23141 /*
23142 * Partition is already locked in the transformPartitionCmdForMerge
23143 * function.
23144 */
23146
23147 /* Create a source tuple slot for the partition being merged. */
23149
23150 /*
23151 * Map computing for moving attributes of the merged partition to the
23152 * new partition.
23153 */
23156
23157 /* Scan through the rows. */
23158 snapshot = RegisterSnapshot(GetLatestSnapshot());
23159 scan = table_beginscan(mergingPartition, snapshot, 0, NULL,
23160 SO_NONE);
23161
23162 /*
23163 * Switch to per-tuple memory context and reset it for each tuple
23164 * produced, so we don't leak memory.
23165 */
23167
23169 {
23171
23173
23174 if (tuple_map)
23175 {
23176 /* Need to use a map to copy attributes. */
23177 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, dstslot);
23178 }
23179 else
23180 {
23182
23183 /* Copy attributes directly. */
23184 insertslot = dstslot;
23185
23187
23188 memcpy(insertslot->tts_values, srcslot->tts_values,
23189 sizeof(Datum) * srcslot->tts_nvalid);
23190 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23191 sizeof(bool) * srcslot->tts_nvalid);
23192
23194 }
23195
23196 /*
23197 * Constraints and GENERATED expressions might reference the
23198 * tableoid column, so fill tts_tableOid with the desired value.
23199 * (We must do this each time, because it gets overwritten with
23200 * newrel's OID during storing.)
23201 */
23202 insertslot->tts_tableOid = RelationGetRelid(newPartRel);
23203
23204 /*
23205 * Now, evaluate any generated expressions whose inputs come from
23206 * the new tuple. We assume these columns won't reference each
23207 * other, so that there's no ordering dependency.
23208 */
23210 insertslot, econtext);
23211
23212 /* Write the tuple out to the new relation. */
23214 ti_options, bistate);
23215
23216 ResetExprContext(econtext);
23217 }
23218
23220 table_endscan(scan);
23221 UnregisterSnapshot(snapshot);
23222
23223 if (tuple_map)
23225
23228 }
23229
23230 FreeExecutorState(estate);
23232 FreeBulkInsertState(bistate);
23233
23235
23236 /*
23237 * We don't need to process this newPartRel since we already processed it
23238 * here, so delete the ALTER TABLE queue for it.
23239 */
23240 foreach(ltab, *wqueue)
23241 {
23242 tab = (AlteredTableInfo *) lfirst(ltab);
23243 if (tab->relid == RelationGetRelid(newPartRel))
23244 {
23246 break;
23247 }
23248 }
23249}
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 8186 of file tablecmds.c.

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

20532{
20534 TupleConstr *constr = RelationGetDescr(scanrel)->constr;
20535 int i;
20536
20537 if (constr && constr->has_not_null)
20538 {
20539 int natts = scanrel->rd_att->natts;
20540
20541 for (i = 1; i <= natts; i++)
20542 {
20543 CompactAttribute *att = TupleDescCompactAttr(scanrel->rd_att, i - 1);
20544
20545 /* invalid not-null constraint must be ignored here */
20546 if (att->attnullability == ATTNULLABLE_VALID && !att->attisdropped)
20547 {
20550
20551 ntest->arg = (Expr *) makeVar(1,
20552 i,
20553 wholeatt->atttypid,
20554 wholeatt->atttypmod,
20555 wholeatt->attcollation,
20556 0);
20557 ntest->nulltesttype = IS_NOT_NULL;
20558
20559 /*
20560 * argisrow=false is correct even for a composite column,
20561 * because attnotnull does not represent a SQL-spec IS NOT
20562 * NULL test in such a case, just IS DISTINCT FROM NULL.
20563 */
20564 ntest->argisrow = false;
20565 ntest->location = -1;
20567 }
20568 }
20569 }
20570
20572}

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

19787{
19788 ListCell *l;
19791
19792 foreach(l, on_commits)
19793 {
19795
19796 /* Ignore entry if already dropped in this xact */
19797 if (oc->deleting_subid != InvalidSubTransactionId)
19798 continue;
19799
19800 switch (oc->oncommit)
19801 {
19802 case ONCOMMIT_NOOP:
19804 /* Do nothing (there shouldn't be such entries, actually) */
19805 break;
19807
19808 /*
19809 * If this transaction hasn't accessed any temporary
19810 * relations, we can skip truncating ON COMMIT DELETE ROWS
19811 * tables, as they must still be empty.
19812 */
19815 break;
19816 case ONCOMMIT_DROP:
19818 break;
19819 }
19820 }
19821
19822 /*
19823 * Truncate relations before dropping so that all dependencies between
19824 * relations are removed after they are worked on. Doing it like this
19825 * might be a waste as it is possible that a relation being truncated will
19826 * be dropped anyway due to its parent being dropped, but this makes the
19827 * code more robust because of not having to re-check that the relation
19828 * exists at truncation time.
19829 */
19830 if (oids_to_truncate != NIL)
19832
19833 if (oids_to_drop != NIL)
19834 {
19836
19837 foreach(l, oids_to_drop)
19838 {
19839 ObjectAddress object;
19840
19841 object.classId = RelationRelationId;
19842 object.objectId = lfirst_oid(l);
19843 object.objectSubId = 0;
19844
19846
19848 }
19849
19850 /*
19851 * Object deletion might involve toast table access (to clean up
19852 * toasted catalog entries), so ensure we have a valid snapshot.
19853 */
19855
19856 /*
19857 * Since this is an automatic drop, rather than one directly initiated
19858 * by the user, we pass the PERFORM_DELETION_INTERNAL flag.
19859 */
19862
19864
19865#ifdef USE_ASSERT_CHECKING
19866
19867 /*
19868 * Note that table deletion will call remove_on_commit_action, so the
19869 * entry should get marked as deleted.
19870 */
19871 foreach(l, on_commits)
19872 {
19874
19875 if (oc->oncommit != ONCOMMIT_DROP)
19876 continue;
19877
19878 Assert(oc->deleting_subid != InvalidSubTransactionId);
19879 }
19880#endif
19881 }
19882}
#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 13591 of file tablecmds.c.

13594{
13596 AlteredTableInfo *tab;
13599
13600 List *children = NIL;
13601 ListCell *child;
13603 Datum val;
13604 char *conbin;
13605
13607 Assert(con->contype == CONSTRAINT_CHECK);
13608
13609 /*
13610 * If we're recursing, the parent has already done this, so skip it. Also,
13611 * if the constraint is a NO INHERIT constraint, we shouldn't try to look
13612 * for it in the children.
13613 */
13614 if (!recursing && !con->connoinherit)
13615 children = find_all_inheritors(RelationGetRelid(rel),
13616 lockmode, NULL);
13617
13618 /*
13619 * For CHECK constraints, we must ensure that we only mark the constraint
13620 * as validated on the parent if it's already validated on the children.
13621 *
13622 * We recurse before validating on the parent, to reduce risk of
13623 * deadlocks.
13624 */
13625 foreach(child, children)
13626 {
13627 Oid childoid = lfirst_oid(child);
13629
13630 if (childoid == RelationGetRelid(rel))
13631 continue;
13632
13633 /*
13634 * If we are told not to recurse, there had better not be any child
13635 * tables, because we can't mark the constraint on the parent valid
13636 * unless it is valid for all child tables.
13637 */
13638 if (!recurse)
13639 ereport(ERROR,
13641 errmsg("constraint must be validated on child tables too")));
13642
13643 /* find_all_inheritors already got lock */
13645
13647 true, lockmode);
13649 }
13650
13651 /* Queue validation for phase 3 */
13653 newcon->name = constrName;
13654 newcon->contype = CONSTR_CHECK;
13655 newcon->refrelid = InvalidOid;
13656 newcon->refindid = InvalidOid;
13657 newcon->conid = con->oid;
13658
13663
13664 /* Find or create work queue entry for this table */
13665 tab = ATGetQueueEntry(wqueue, rel);
13666 tab->constraints = lappend(tab->constraints, newcon);
13667
13668 /*
13669 * Invalidate relcache so that others see the new validated constraint.
13670 */
13672
13673 /*
13674 * Now update the catalog, while we have the door open.
13675 */
13678 copy_con->convalidated = true;
13680
13682
13684}

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

13471{
13473 AlteredTableInfo *tab;
13476
13477 /* since this function recurses, it could be driven to stack overflow */
13479
13481 Assert(con->contype == CONSTRAINT_FOREIGN);
13482 Assert(!con->convalidated);
13483
13484 /*
13485 * Add the validation to phase 3's queue; not needed for partitioned
13486 * tables themselves, only for their partitions.
13487 *
13488 * When the referenced table (pkrelid) is partitioned, the referencing
13489 * table (fkrel) has one pg_constraint row pointing to each partition
13490 * thereof. These rows are there only to support action triggers and no
13491 * table scan is needed, therefore skip this for them as well.
13492 */
13493 if (fkrel->rd_rel->relkind == RELKIND_RELATION &&
13494 con->confrelid == pkrelid)
13495 {
13498
13499 /* Queue validation for phase 3 */
13501 /* for now this is all we need */
13502 fkconstraint->conname = pstrdup(NameStr(con->conname));
13503
13505 newcon->name = fkconstraint->conname;
13506 newcon->contype = CONSTR_FOREIGN;
13507 newcon->refrelid = con->confrelid;
13508 newcon->refindid = con->conindid;
13509 newcon->conid = con->oid;
13510 newcon->qual = (Node *) fkconstraint;
13511
13512 /* Find or create work queue entry for this table */
13514 tab->constraints = lappend(tab->constraints, newcon);
13515 }
13516
13517 /*
13518 * If the table at either end of the constraint is partitioned, we need to
13519 * recurse and handle every unvalidated constraint that is a child of this
13520 * constraint.
13521 */
13522 if (fkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
13523 get_rel_relkind(con->confrelid) == RELKIND_PARTITIONED_TABLE)
13524 {
13525 ScanKeyData pkey;
13528
13529 ScanKeyInit(&pkey,
13532 ObjectIdGetDatum(con->oid));
13533
13535 true, NULL, 1, &pkey);
13536
13538 {
13541
13543
13544 /*
13545 * If the child constraint has already been validated, no further
13546 * action is required for it or its descendants, as they are all
13547 * valid.
13548 */
13549 if (childcon->convalidated)
13550 continue;
13551
13552 childrel = table_open(childcon->conrelid, lockmode);
13553
13554 /*
13555 * NB: Note that pkrelid should be passed as-is during recursion,
13556 * as it is required to identify the root referenced table.
13557 */
13559 childtup, lockmode);
13561 }
13562
13564 }
13565
13566 /*
13567 * Now mark the pg_constraint row as validated (even if we didn't check,
13568 * notably the ones for partitions on the referenced side).
13569 *
13570 * We rely on transaction abort to roll back this change if phase 3
13571 * ultimately finds violating rows. This is a bit ugly.
13572 */
13575 copy_con->convalidated = true;
13577
13579
13581}

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

13697{
13699 AlteredTableInfo *tab;
13702 List *children = NIL;
13704 char *colname;
13705
13707 Assert(con->contype == CONSTRAINT_NOTNULL);
13708
13710
13711 /*
13712 * If we're recursing, we've already done this for parent, so skip it.
13713 * Also, if the constraint is a NO INHERIT constraint, we shouldn't try to
13714 * look for it in the children.
13715 *
13716 * We recurse before validating on the parent, to reduce risk of
13717 * deadlocks.
13718 */
13719 if (!recursing && !con->connoinherit)
13720 children = find_all_inheritors(RelationGetRelid(rel), lockmode, NULL);
13721
13722 colname = get_attname(RelationGetRelid(rel), attnum, false);
13723 foreach_oid(childoid, children)
13724 {
13728 char *conname;
13729
13730 if (childoid == RelationGetRelid(rel))
13731 continue;
13732
13733 /*
13734 * If we are told not to recurse, there had better not be any child
13735 * tables, because we can't mark the constraint on the parent valid
13736 * unless it is valid for all child tables.
13737 */
13738 if (!recurse)
13739 ereport(ERROR,
13741 errmsg("constraint must be validated on child tables too"));
13742
13743 /*
13744 * The column on child might have a different attnum, so search by
13745 * column name.
13746 */
13748 if (!contup)
13749 elog(ERROR, "cache lookup failed for not-null constraint on column \"%s\" of relation \"%s\"",
13750 colname, get_rel_name(childoid));
13752 if (childcon->convalidated)
13753 continue;
13754
13755 /* find_all_inheritors already got lock */
13757 conname = pstrdup(NameStr(childcon->conname));
13758
13759 /* XXX improve ATExecValidateConstraint API to avoid double search */
13761 false, true, lockmode);
13763 }
13764
13765 /* Set attnotnull appropriately without queueing another validation */
13766 set_attnotnull(NULL, rel, attnum, true, false);
13767
13768 tab = ATGetQueueEntry(wqueue, rel);
13769 tab->verify_new_notnull = true;
13770
13771 /*
13772 * Invalidate relcache so that others see the new validated constraint.
13773 */
13775
13776 /*
13777 * Now update the catalogs, while we have the door open.
13778 */
13781 copy_con->convalidated = true;
13783
13785
13787}

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

20651{
20652 /*
20653 * Based on the table's existing constraints, determine whether or not we
20654 * may skip scanning the table.
20655 */
20657 {
20658 if (!validate_default)
20660 (errmsg_internal("partition constraint for table \"%s\" is implied by existing constraints",
20661 RelationGetRelationName(scanrel))));
20662 else
20664 (errmsg_internal("updated partition constraint for default partition \"%s\" is implied by existing constraints",
20665 RelationGetRelationName(scanrel))));
20666 return;
20667 }
20668
20669 /*
20670 * Constraints proved insufficient. For plain relations, queue a
20671 * validation item now; for partitioned tables, recurse to process each
20672 * partition.
20673 */
20674 if (scanrel->rd_rel->relkind == RELKIND_RELATION)
20675 {
20676 AlteredTableInfo *tab;
20677
20678 /* Grab a work queue entry. */
20679 tab = ATGetQueueEntry(wqueue, scanrel);
20682 tab->validate_default = validate_default;
20683 }
20684 else if (scanrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
20685 {
20686 PartitionDesc partdesc = RelationGetPartitionDesc(scanrel, true);
20687 int i;
20688
20689 for (i = 0; i < partdesc->nparts; i++)
20690 {
20693
20694 /*
20695 * This is the minimum lock we need to prevent deadlocks.
20696 */
20698
20699 /*
20700 * Adjust the constraint for scanrel so that it matches this
20701 * partition's attribute numbers.
20702 */
20705 part_rel, scanrel);
20706
20709 validate_default);
20710 table_close(part_rel, NoLock); /* keep lock till commit */
20711 }
20712 }
20713}
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 20052 of file tablecmds.c.

20054{
20055 Node *stmt = (Node *) arg;
20056 ObjectType reltype;
20057 HeapTuple tuple;
20060 char relkind;
20061
20062 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
20063 if (!HeapTupleIsValid(tuple))
20064 return; /* concurrently dropped */
20066 relkind = classform->relkind;
20067
20068 /* Must own relation. */
20071
20072 /* No system table modifications unless explicitly allowed. */
20074 ereport(ERROR,
20076 errmsg("permission denied: \"%s\" is a system catalog",
20077 rv->relname)));
20078
20079 /*
20080 * Extract the specified relation type from the statement parse tree.
20081 *
20082 * Also, for ALTER .. RENAME, check permissions: the user must (still)
20083 * have CREATE rights on the containing namespace.
20084 */
20085 if (IsA(stmt, RenameStmt))
20086 {
20089 if (aclresult != ACLCHECK_OK)
20091 get_namespace_name(classform->relnamespace));
20092 reltype = ((RenameStmt *) stmt)->renameType;
20093 }
20094 else if (IsA(stmt, AlterObjectSchemaStmt))
20095 reltype = ((AlterObjectSchemaStmt *) stmt)->objectType;
20096
20097 else if (IsA(stmt, AlterTableStmt))
20098 reltype = ((AlterTableStmt *) stmt)->objtype;
20099 else
20100 {
20101 elog(ERROR, "unrecognized node type: %d", (int) nodeTag(stmt));
20102 reltype = OBJECT_TABLE; /* placate compiler */
20103 }
20104
20105 /*
20106 * For compatibility with prior releases, we allow ALTER TABLE to be used
20107 * with most other types of relations (but not composite types). We allow
20108 * similar flexibility for ALTER INDEX in the case of RENAME, but not
20109 * otherwise. Otherwise, the user must select the correct form of the
20110 * command for the relation at issue.
20111 */
20112 if (reltype == OBJECT_SEQUENCE && relkind != RELKIND_SEQUENCE)
20113 ereport(ERROR,
20115 errmsg("\"%s\" is not a sequence", rv->relname)));
20116
20117 if (reltype == OBJECT_VIEW && relkind != RELKIND_VIEW)
20118 ereport(ERROR,
20120 errmsg("\"%s\" is not a view", rv->relname)));
20121
20122 if (reltype == OBJECT_MATVIEW && relkind != RELKIND_MATVIEW)
20123 ereport(ERROR,
20125 errmsg("\"%s\" is not a materialized view", rv->relname)));
20126
20127 if (reltype == OBJECT_FOREIGN_TABLE && relkind != RELKIND_FOREIGN_TABLE)
20128 ereport(ERROR,
20130 errmsg("\"%s\" is not a foreign table", rv->relname)));
20131
20132 if (reltype == OBJECT_TYPE && relkind != RELKIND_COMPOSITE_TYPE)
20133 ereport(ERROR,
20135 errmsg("\"%s\" is not a composite type", rv->relname)));
20136
20137 if (reltype == OBJECT_PROPGRAPH && relkind != RELKIND_PROPGRAPH)
20138 ereport(ERROR,
20140 errmsg("\"%s\" is not a property graph", rv->relname)));
20141
20142 if (reltype == OBJECT_INDEX && relkind != RELKIND_INDEX &&
20143 relkind != RELKIND_PARTITIONED_INDEX
20144 && !IsA(stmt, RenameStmt))
20145 ereport(ERROR,
20147 errmsg("\"%s\" is not an index", rv->relname)));
20148
20149 /*
20150 * Don't allow ALTER TABLE on composite types. We want people to use ALTER
20151 * TYPE for that.
20152 */
20153 if (reltype != OBJECT_TYPE && relkind == RELKIND_COMPOSITE_TYPE)
20154 ereport(ERROR,
20156 errmsg("\"%s\" is a composite type", rv->relname),
20157 /* translator: %s is an SQL ALTER command */
20158 errhint("Use %s instead.",
20159 "ALTER TYPE")));
20160
20161 /*
20162 * Don't allow ALTER TABLE .. SET SCHEMA on relations that can't be moved
20163 * to a different schema, such as indexes and TOAST tables.
20164 */
20166 {
20167 if (relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX)
20168 ereport(ERROR,
20170 errmsg("cannot change schema of index \"%s\"",
20171 rv->relname),
20172 errhint("Change the schema of the table instead.")));
20173 else if (relkind == RELKIND_COMPOSITE_TYPE)
20174 ereport(ERROR,
20176 errmsg("cannot change schema of composite type \"%s\"",
20177 rv->relname),
20178 /* translator: %s is an SQL ALTER command */
20179 errhint("Use %s instead.",
20180 "ALTER TYPE")));
20181 else if (relkind == RELKIND_TOASTVALUE)
20182 ereport(ERROR,
20184 errmsg("cannot change schema of TOAST table \"%s\"",
20185 rv->relname),
20186 errhint("Change the schema of the table instead.")));
20187 }
20188
20189 ReleaseSysCache(tuple);
20190}
bool IsSystemClass(Oid relid, Form_pg_class reltuple)
Definition catalog.c:86
ObjectType
@ OBJECT_PROPGRAPH
@ OBJECT_FOREIGN_TABLE
@ OBJECT_VIEW
@ OBJECT_TYPE

References ACL_CREATE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, allowSystemTableMods, arg, elog, ereport, errcode(), errhint(), errmsg, ERROR, fb(), get_namespace_name(), get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), HeapTupleIsValid, IsA, IsSystemClass(), nodeTag, object_aclcheck(), OBJECT_FOREIGN_TABLE, OBJECT_INDEX, OBJECT_MATVIEW, object_ownercheck(), OBJECT_PROPGRAPH, OBJECT_SCHEMA, OBJECT_SEQUENCE, OBJECT_TABLE, OBJECT_TYPE, OBJECT_VIEW, ObjectIdGetDatum(), ReleaseSysCache(), RangeVar::relname, SearchSysCache1(), and stmt.

Referenced by AlterTableLookupRelation(), AlterTableNamespace(), and RenameRelation().

◆ RangeVarCallbackForAttachIndex()

static void RangeVarCallbackForAttachIndex ( const RangeVar rv,
Oid  relOid,
Oid  oldRelOid,
void arg 
)
static

Definition at line 22052 of file tablecmds.c.

22054{
22057 HeapTuple tuple;
22058
22059 state = (struct AttachIndexCallbackState *) arg;
22060
22061 if (!state->lockedParentTbl)
22062 {
22063 LockRelationOid(state->parentTblOid, AccessShareLock);
22064 state->lockedParentTbl = true;
22065 }
22066
22067 /*
22068 * If we previously locked some other heap, and the name we're looking up
22069 * no longer refers to an index on that relation, release the now-useless
22070 * lock. XXX maybe we should do *after* we verify whether the index does
22071 * not actually belong to the same relation ...
22072 */
22073 if (relOid != oldRelOid && OidIsValid(state->partitionOid))
22074 {
22075 UnlockRelationOid(state->partitionOid, AccessShareLock);
22076 state->partitionOid = InvalidOid;
22077 }
22078
22079 /* Didn't find a relation, so no need for locking or permission checks. */
22080 if (!OidIsValid(relOid))
22081 return;
22082
22083 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid));
22084 if (!HeapTupleIsValid(tuple))
22085 return; /* concurrently dropped, so nothing to do */
22087 if (classform->relkind != RELKIND_PARTITIONED_INDEX &&
22088 classform->relkind != RELKIND_INDEX)
22089 ereport(ERROR,
22091 errmsg("\"%s\" is not an index", rv->relname)));
22092 ReleaseSysCache(tuple);
22093
22094 /*
22095 * Since we need only examine the heap's tupledesc, an access share lock
22096 * on it (preventing any DDL) is sufficient.
22097 */
22098 state->partitionOid = IndexGetRelation(relOid, false);
22099 LockRelationOid(state->partitionOid, AccessShareLock);
22100}
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 1764 of file tablecmds.c.

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

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

4055{
4056 HeapTuple tuple;
4058
4059 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
4060 if (!HeapTupleIsValid(tuple))
4061 return; /* concurrently dropped */
4062 form = (Form_pg_class) GETSTRUCT(tuple);
4063 renameatt_check(relid, form, false);
4064 ReleaseSysCache(tuple);
4065}
static void renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
Definition tablecmds.c:3859

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

19998{
19999 HeapTuple tuple;
20000
20001 /* Nothing to do if the relation was not found. */
20002 if (!OidIsValid(relId))
20003 return;
20004
20005 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
20006 if (!HeapTupleIsValid(tuple)) /* should not happen */
20007 elog(ERROR, "cache lookup failed for relation %u", relId);
20008
20011
20012 ReleaseSysCache(tuple);
20013}

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

19962{
19963 char relkind;
19965
19966 /* Nothing to do if the relation was not found. */
19967 if (!OidIsValid(relId))
19968 return;
19969
19970 /*
19971 * If the relation does exist, check whether it's an index. But note that
19972 * the relation might have been dropped between the time we did the name
19973 * lookup and now. In that case, there's nothing to do.
19974 */
19975 relkind = get_rel_relkind(relId);
19976 if (!relkind)
19977 return;
19978 if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
19979 relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
19980 ereport(ERROR,
19982 errmsg("\"%s\" is not a table or materialized view", relation->relname)));
19983
19984 /* Check permissions */
19986 if (aclresult != ACLCHECK_OK)
19989 relation->relname);
19990}
#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 20020 of file tablecmds.c.

20022{
20023 HeapTuple tuple;
20024
20025 /* Nothing to do if the relation was not found. */
20026 if (!OidIsValid(relId))
20027 return;
20028
20029 tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId));
20030 if (!HeapTupleIsValid(tuple)) /* should not happen */
20031 elog(ERROR, "cache lookup failed for relation %u", relId);
20032
20035 relation->relname);
20036
20037 if (!allowSystemTableMods &&
20038 IsSystemClass(relId, (Form_pg_class) GETSTRUCT(tuple)))
20039 ereport(ERROR,
20041 errmsg("permission denied: \"%s\" is a system catalog",
20042 relation->relname)));
20043
20044 ReleaseSysCache(tuple);
20045}

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, elog, ereport, errcode(), errmsg, ERROR, fb(), get_rel_relkind(), get_relkind_objtype(), GETSTRUCT(), GetUserId(), HeapTupleIsValid, IsSystemClass(), object_ownercheck(), ObjectIdGetDatum(), OidIsValid, ReleaseSysCache(), RangeVar::relname, and SearchSysCache1().

Referenced by AlterPropGraph(), AlterSequence(), CreatePropGraph(), ProcessUtilitySlow(), transformPartitionCmdForMerge(), and transformPartitionCmdForSplit().

◆ RebuildConstraintComment()

static void RebuildConstraintComment ( AlteredTableInfo tab,
AlterTablePass  pass,
Oid  objid,
Relation  rel,
List domname,
const char conname 
)
static

Definition at line 16316 of file tablecmds.c.

16319{
16320 CommentStmt *cmd;
16321 char *comment_str;
16323
16324 /* Look for comment for object wanted, and leave if none */
16326 if (comment_str == NULL)
16327 return;
16328
16329 /* Build CommentStmt node, copying all input data for safety */
16330 cmd = makeNode(CommentStmt);
16331 if (rel)
16332 {
16334 cmd->object = (Node *)
16337 makeString(pstrdup(conname)));
16338 }
16339 else
16340 {
16342 cmd->object = (Node *)
16344 makeString(pstrdup(conname)));
16345 }
16346 cmd->comment = comment_str;
16347
16348 /* Append it to list of commands */
16350 newcmd->subtype = AT_ReAddComment;
16351 newcmd->def = (Node *) cmd;
16352 tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
16353}
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 22279 of file tablecmds.c.

22280{
22282
22283 existingIdx = index_get_partition(partitionTbl,
22284 RelationGetRelid(parentIdx));
22286 ereport(ERROR,
22288 errmsg("cannot attach index \"%s\" as a partition of index \"%s\"",
22290 RelationGetRelationName(parentIdx)),
22291 errdetail("Another index \"%s\" is already attached for partition \"%s\".",
22293 RelationGetRelationName(partitionTbl))));
22294}

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

19728{
19731
19732 /*
19733 * We needn't bother registering the relation unless there is an ON COMMIT
19734 * action we need to take.
19735 */
19736 if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS)
19737 return;
19738
19740
19742 oc->relid = relid;
19743 oc->oncommit = action;
19744 oc->creating_subid = GetCurrentSubTransactionId();
19745 oc->deleting_subid = InvalidSubTransactionId;
19746
19747 /*
19748 * We use lcons() here so that ON COMMIT actions are processed in reverse
19749 * order of registration. That might not be essential but it seems
19750 * reasonable.
19751 */
19753
19755}
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 18868 of file tablecmds.c.

18870{
18877 ListCell *index;
18878
18879 /*
18880 * Check whether relreplident has changed, and update it if so.
18881 */
18886 elog(ERROR, "cache lookup failed for relation \"%s\"",
18889 if (pg_class_form->relreplident != ri_type)
18890 {
18891 pg_class_form->relreplident = ri_type;
18893 }
18896
18897 /*
18898 * Update the per-index indisreplident flags correctly.
18899 */
18901 foreach(index, RelationGetIndexList(rel))
18902 {
18904 bool dirty = false;
18905
18909 elog(ERROR, "cache lookup failed for index %u", thisIndexOid);
18911
18912 if (thisIndexOid == indexOid)
18913 {
18914 /* Set the bit if not already set. */
18915 if (!pg_index_form->indisreplident)
18916 {
18917 dirty = true;
18918 pg_index_form->indisreplident = true;
18919 }
18920 }
18921 else
18922 {
18923 /* Unset the bit if set. */
18924 if (pg_index_form->indisreplident)
18925 {
18926 dirty = true;
18927 pg_index_form->indisreplident = false;
18928 }
18929 }
18930
18931 if (dirty)
18932 {
18935 InvalidOid, is_internal);
18936
18937 /*
18938 * Invalidate the relcache for the table, so that after we commit
18939 * all sessions will refresh the table's replica identity index
18940 * before attempting any UPDATE or DELETE on the table. (If we
18941 * changed the table's pg_class row above, then a relcache inval
18942 * is already queued due to that; but we might not have.)
18943 */
18945 }
18947 }
18948
18950}
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 15517 of file tablecmds.c.

15519{
15521 ScanKeyData key[3];
15522 SysScanDesc scan;
15524
15525 Assert(subtype == AT_AlterColumnType || subtype == AT_SetExpression);
15526
15528
15529 ScanKeyInit(&key[0],
15533 ScanKeyInit(&key[1],
15537 ScanKeyInit(&key[2],
15541
15543 NULL, 3, key);
15544
15546 {
15549
15550 foundObject.classId = foundDep->classid;
15551 foundObject.objectId = foundDep->objid;
15552 foundObject.objectSubId = foundDep->objsubid;
15553
15554 switch (foundObject.classId)
15555 {
15556 case RelationRelationId:
15557 {
15558 char relKind = get_rel_relkind(foundObject.objectId);
15559
15560 if (relKind == RELKIND_INDEX ||
15562 {
15563 Assert(foundObject.objectSubId == 0);
15565 }
15566 else if (relKind == RELKIND_SEQUENCE)
15567 {
15568 /*
15569 * This must be a SERIAL column's sequence. We need
15570 * not do anything to it.
15571 */
15572 Assert(foundObject.objectSubId == 0);
15573 }
15574 else
15575 {
15576 /* Not expecting any other direct dependencies... */
15577 elog(ERROR, "unexpected object depending on column: %s",
15579 }
15580 break;
15581 }
15582
15584 Assert(foundObject.objectSubId == 0);
15586 break;
15587
15589
15590 /*
15591 * A new-style SQL function can depend on a column, if that
15592 * column is referenced in the parsed function body. Ideally
15593 * we'd automatically update the function by deparsing and
15594 * reparsing it, but that's risky and might well fail anyhow.
15595 * FIXME someday.
15596 *
15597 * This is only a problem for AT_AlterColumnType, not
15598 * AT_SetExpression.
15599 */
15600 if (subtype == AT_AlterColumnType)
15601 ereport(ERROR,
15603 errmsg("cannot alter type of a column used by a function or procedure"),
15604 errdetail("%s depends on column \"%s\"",
15606 colName)));
15607 break;
15608
15609 case RewriteRelationId:
15610
15611 /*
15612 * View/rule bodies have pretty much the same issues as
15613 * function bodies. FIXME someday.
15614 */
15615 if (subtype == AT_AlterColumnType)
15616 ereport(ERROR,
15618 errmsg("cannot alter type of a column used by a view or rule"),
15619 errdetail("%s depends on column \"%s\"",
15621 colName)));
15622 break;
15623
15624 case TriggerRelationId:
15625
15626 /*
15627 * A trigger can depend on a column because the column is
15628 * specified as an update target, or because the column is
15629 * used in the trigger's WHEN condition. The first case would
15630 * not require any extra work, but the second case would
15631 * require updating the WHEN expression, which has the same
15632 * issues as above. Since we can't easily tell which case
15633 * applies, we punt for both. FIXME someday.
15634 */
15635 if (subtype == AT_AlterColumnType)
15636 ereport(ERROR,
15638 errmsg("cannot alter type of a column used in a trigger definition"),
15639 errdetail("%s depends on column \"%s\"",
15641 colName)));
15642 break;
15643
15644 case PolicyRelationId:
15645
15646 /*
15647 * A policy can depend on a column because the column is
15648 * specified in the policy's USING or WITH CHECK qual
15649 * expressions. It might be possible to rewrite and recheck
15650 * the policy expression, but punt for now. It's certainly
15651 * easy enough to remove and recreate the policy; still, FIXME
15652 * someday.
15653 */
15654 if (subtype == AT_AlterColumnType)
15655 ereport(ERROR,
15657 errmsg("cannot alter type of a column used in a policy definition"),
15658 errdetail("%s depends on column \"%s\"",
15660 colName)));
15661 break;
15662
15664 {
15666
15667 if (col.objectId == RelationGetRelid(rel) &&
15668 col.objectSubId == attnum)
15669 {
15670 /*
15671 * Ignore the column's own default expression. The
15672 * caller deals with it.
15673 */
15674 }
15675 else
15676 {
15677 /*
15678 * This must be a reference from the expression of a
15679 * generated column elsewhere in the same table.
15680 * Changing the type/generated expression of a column
15681 * that is used by a generated column is not allowed
15682 * by SQL standard, so just punt for now. It might be
15683 * doable with some thinking and effort.
15684 */
15685 if (subtype == AT_AlterColumnType)
15686 ereport(ERROR,
15688 errmsg("cannot alter type of a column used by a generated column"),
15689 errdetail("Column \"%s\" is used by generated column \"%s\".",
15690 colName,
15691 get_attname(col.objectId,
15692 col.objectSubId,
15693 false))));
15694 }
15695 break;
15696 }
15697
15699
15700 /*
15701 * Give the extended-stats machinery a chance to fix anything
15702 * that this column type change would break.
15703 */
15705 break;
15706
15708
15709 /*
15710 * Column reference in a PUBLICATION ... FOR TABLE ... WHERE
15711 * clause. Same issues as above. FIXME someday.
15712 */
15713 if (subtype == AT_AlterColumnType)
15714 ereport(ERROR,
15716 errmsg("cannot alter type of a column used by a publication WHERE clause"),
15717 errdetail("%s depends on column \"%s\"",
15719 colName)));
15720 break;
15721
15722 default:
15723
15724 /*
15725 * We don't expect any other sorts of objects to depend on a
15726 * column.
15727 */
15728 elog(ERROR, "unexpected object depending on column: %s",
15730 break;
15731 }
15732 }
15733
15734 systable_endscan(scan);
15736}
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 15758 of file tablecmds.c.

15759{
15760 if (!get_index_isclustered(indoid))
15761 return;
15762
15763 if (tab->clusterOnIndex)
15764 elog(ERROR, "relation %u has multiple clustered indexes", tab->relid);
15765
15766 tab->clusterOnIndex = get_rel_name(indoid);
15767}
bool get_index_isclustered(Oid index_oid)
Definition lsyscache.c:3954

References AlteredTableInfo::clusterOnIndex, elog, ERROR, get_index_isclustered(), get_rel_name(), and AlteredTableInfo::relid.

Referenced by RememberConstraintForRebuilding(), and RememberIndexForRebuilding().

◆ RememberConstraintForRebuilding()

static void RememberConstraintForRebuilding ( Oid  conoid,
AlteredTableInfo tab 
)
static

Definition at line 15774 of file tablecmds.c.

15775{
15776 /*
15777 * This de-duplication check is critical for two independent reasons: we
15778 * mustn't try to recreate the same constraint twice, and if a constraint
15779 * depends on more than one column whose type is to be altered, we must
15780 * capture its definition string before applying any of the column type
15781 * changes. ruleutils.c will get confused if we ask again later.
15782 */
15783 if (!list_member_oid(tab->changedConstraintOids, conoid))
15784 {
15785 /* OK, capture the constraint's existing definition string */
15787 Oid indoid;
15788
15789 /*
15790 * It is critical to create not-null constraints ahead of primary key
15791 * indexes; otherwise, the not-null constraint would be created by the
15792 * primary key, and the constraint name would be wrong.
15793 */
15795 {
15796 tab->changedConstraintOids = lcons_oid(conoid,
15800 }
15801 else
15802 {
15803
15805 conoid);
15807 defstring);
15808 }
15809
15810 /*
15811 * For the index of a constraint, if any, remember if it is used for
15812 * the table's replica identity or if it is a clustered index, so that
15813 * ATPostAlterTypeCleanup() can queue up commands necessary to restore
15814 * those properties.
15815 */
15816 indoid = get_constraint_index(conoid);
15817 if (OidIsValid(indoid))
15818 {
15820 RememberClusterOnForRebuilding(indoid, tab);
15821 }
15822 }
15823}
List * lcons_oid(Oid datum, List *list)
Definition list.c:531
char * pg_get_constraintdef_command(Oid constraintId)
Definition ruleutils.c:2542
static void RememberReplicaIdentityForRebuilding(Oid indoid, AlteredTableInfo *tab)
static void RememberClusterOnForRebuilding(Oid indoid, AlteredTableInfo *tab)

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

Referenced by RememberAllDependentForRebuilding(), and RememberIndexForRebuilding().

◆ RememberIndexForRebuilding()

static void RememberIndexForRebuilding ( Oid  indoid,
AlteredTableInfo tab 
)
static

Definition at line 15830 of file tablecmds.c.

15831{
15832 /*
15833 * This de-duplication check is critical for two independent reasons: we
15834 * mustn't try to recreate the same index twice, and if an index depends
15835 * on more than one column whose type is to be altered, we must capture
15836 * its definition string before applying any of the column type changes.
15837 * ruleutils.c will get confused if we ask again later.
15838 */
15839 if (!list_member_oid(tab->changedIndexOids, indoid))
15840 {
15841 /*
15842 * Before adding it as an index-to-rebuild, we'd better see if it
15843 * belongs to a constraint, and if so rebuild the constraint instead.
15844 * Typically this check fails, because constraint indexes normally
15845 * have only dependencies on their constraint. But it's possible for
15846 * such an index to also have direct dependencies on table columns,
15847 * for example with a partial exclusion constraint.
15848 */
15849 Oid conoid = get_index_constraint(indoid);
15850
15851 if (OidIsValid(conoid))
15852 {
15854 }
15855 else
15856 {
15857 /* OK, capture the index's existing definition string */
15858 char *defstring = pg_get_indexdef_string(indoid);
15859
15861 indoid);
15863 defstring);
15864
15865 /*
15866 * Remember if this index is used for the table's replica identity
15867 * or if it is a clustered index, so that ATPostAlterTypeCleanup()
15868 * can queue up commands necessary to restore those properties.
15869 */
15871 RememberClusterOnForRebuilding(indoid, tab);
15872 }
15873 }
15874}
Oid get_index_constraint(Oid indexId)
Definition pg_depend.c:1192
char * pg_get_indexdef_string(Oid indexrelid)
Definition ruleutils.c:1236

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

Referenced by RememberAllDependentForRebuilding().

◆ RememberReplicaIdentityForRebuilding()

static void RememberReplicaIdentityForRebuilding ( Oid  indoid,
AlteredTableInfo tab 
)
static

Definition at line 15743 of file tablecmds.c.

15744{
15745 if (!get_index_isreplident(indoid))
15746 return;
15747
15748 if (tab->replicaIdentityIndex)
15749 elog(ERROR, "relation %u has multiple indexes marked as replica identity", tab->relid);
15750
15751 tab->replicaIdentityIndex = get_rel_name(indoid);
15752}
bool get_index_isreplident(Oid index_oid)
Definition lsyscache.c:3908

References elog, ERROR, get_index_isreplident(), get_rel_name(), AlteredTableInfo::relid, and AlteredTableInfo::replicaIdentityIndex.

Referenced by RememberConstraintForRebuilding(), and RememberIndexForRebuilding().

◆ RememberStatisticsForRebuilding()

static void RememberStatisticsForRebuilding ( Oid  stxoid,
AlteredTableInfo tab 
)
static

Definition at line 15881 of file tablecmds.c.

15882{
15883 /*
15884 * This de-duplication check is critical for two independent reasons: we
15885 * mustn't try to recreate the same statistics object twice, and if the
15886 * statistics object depends on more than one column whose type is to be
15887 * altered, we must capture its definition string before applying any of
15888 * the type changes. ruleutils.c will get confused if we ask again later.
15889 */
15891 {
15892 /* OK, capture the statistics object's existing definition string */
15894
15896 stxoid);
15898 defstring);
15899 }
15900}
char * pg_get_statisticsobjdef_string(Oid statextid)
Definition ruleutils.c:1985

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

Referenced by RememberAllDependentForRebuilding().

◆ remove_on_commit_action()

void remove_on_commit_action ( Oid  relid)

Definition at line 19763 of file tablecmds.c.

19764{
19765 ListCell *l;
19766
19767 foreach(l, on_commits)
19768 {
19770
19771 if (oc->relid == relid)
19772 {
19774 break;
19775 }
19776 }
19777}
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 18416 of file tablecmds.c.

18417{
18419 SysScanDesc scan;
18420 ScanKeyData key[3];
18423 AttrMap *attmap;
18424 List *connames;
18425 List *nncolumns;
18426 bool found;
18427 bool is_partitioning;
18428
18430
18435 if (!found)
18436 {
18437 if (is_partitioning)
18438 ereport(ERROR,
18440 errmsg("relation \"%s\" is not a partition of relation \"%s\"",
18443 else
18444 ereport(ERROR,
18446 errmsg("relation \"%s\" is not a parent of relation \"%s\"",
18449 }
18450
18451 /*
18452 * Search through child columns looking for ones matching parent rel
18453 */
18455 ScanKeyInit(&key[0],
18460 true, NULL, 1, key);
18462 {
18464
18465 /* Ignore if dropped or not inherited */
18466 if (att->attisdropped)
18467 continue;
18468 if (att->attinhcount <= 0)
18469 continue;
18470
18472 NameStr(att->attname)))
18473 {
18474 /* Decrement inhcount and possibly set islocal to true */
18477
18478 copy_att->attinhcount--;
18479 if (copy_att->attinhcount == 0)
18480 copy_att->attislocal = true;
18481
18484 }
18485 }
18486 systable_endscan(scan);
18488
18489 /*
18490 * Likewise, find inherited check and not-null constraints and disinherit
18491 * them. To do this, we first need a list of the names of the parent's
18492 * check constraints. (We cheat a bit by only checking for name matches,
18493 * assuming that the expressions will match.)
18494 *
18495 * For NOT NULL columns, we store column numbers to match, mapping them in
18496 * to the child rel's attribute numbers.
18497 */
18500 false);
18501
18503 ScanKeyInit(&key[0],
18508 true, NULL, 1, key);
18509
18510 connames = NIL;
18511 nncolumns = NIL;
18512
18514 {
18516
18517 if (con->connoinherit)
18518 continue;
18519
18520 if (con->contype == CONSTRAINT_CHECK)
18521 connames = lappend(connames, pstrdup(NameStr(con->conname)));
18522 if (con->contype == CONSTRAINT_NOTNULL)
18523 {
18525
18527 }
18528 }
18529
18530 systable_endscan(scan);
18531
18532 /* Now scan the child's constraints to find matches */
18533 ScanKeyInit(&key[0],
18538 true, NULL, 1, key);
18539
18541 {
18543 bool match = false;
18544
18545 /*
18546 * Match CHECK constraints by name, not-null constraints by column
18547 * number, and ignore all others.
18548 */
18549 if (con->contype == CONSTRAINT_CHECK)
18550 {
18552 {
18553 if (con->contype == CONSTRAINT_CHECK &&
18554 strcmp(NameStr(con->conname), chkname) == 0)
18555 {
18556 match = true;
18558 break;
18559 }
18560 }
18561 }
18562 else if (con->contype == CONSTRAINT_NOTNULL)
18563 {
18565
18567 {
18568 if (prevattno == child_attno)
18569 {
18570 match = true;
18572 break;
18573 }
18574 }
18575 }
18576 else
18577 continue;
18578
18579 if (match)
18580 {
18581 /* Decrement inhcount and possibly set islocal to true */
18584
18585 if (copy_con->coninhcount <= 0) /* shouldn't happen */
18586 elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
18588
18589 copy_con->coninhcount--;
18590 if (copy_con->coninhcount == 0)
18591 copy_con->conislocal = true;
18592
18595 }
18596 }
18597
18598 /* We should have matched all constraints */
18599 if (connames != NIL || nncolumns != NIL)
18600 elog(ERROR, "%d unmatched constraints while removing inheritance from \"%s\" to \"%s\"",
18603
18604 systable_endscan(scan);
18606
18611
18612 /*
18613 * Post alter hook of this inherits. Since object_access_hook doesn't take
18614 * multiple object identifiers, we relay oid of parent relation using
18615 * auxiliary_id argument.
18616 */
18620}
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 12006 of file tablecmds.c.

12008{
12012 SysScanDesc scan;
12014
12015 ScanKeyInit(&key,
12018 ObjectIdGetDatum(conrelid));
12019
12022 true, NULL, 1, &key);
12024 while ((consttup = systable_getnext(scan)) != NULL)
12025 {
12027
12028 if (conform->conparentid != conoid)
12029 continue;
12030 else
12031 {
12032 ObjectAddress addr;
12036
12039
12040 /*
12041 * First we must delete the dependency record that binds the
12042 * constraint records together.
12043 */
12045 conform->oid,
12048 conoid);
12049 Assert(n == 1); /* actually only one is expected */
12050
12051 /*
12052 * Now search for the triggers for this constraint and set them up
12053 * for deletion too
12054 */
12060 true, NULL, 1, &key2);
12061 while ((trigtup = systable_getnext(scan2)) != NULL)
12062 {
12066 }
12068 }
12069 }
12070 /* make the dependency deletions visible */
12074 systable_endscan(scan);
12075}
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:305
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 1596 of file tablecmds.c.

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

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

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

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

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

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

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

References aclcheck_error(), ACLCHECK_NOT_OWNER, allowSystemTableMods, ereport, errcode(), errdetail_relkind_not_supported(), errmsg, ERROR, fb(), get_rel_relkind(), get_relkind_objtype(), GetUserId(), IsSystemClass(), NameStr, and object_ownercheck().

Referenced by RangeVarCallbackForRenameAttribute(), rename_constraint_internal(), and renameatt_internal().

◆ renameatt_internal()

static AttrNumber renameatt_internal ( Oid  myrelid,
const char oldattname,
const char newattname,
bool  recurse,
bool  recursing,
int  expected_parents,
DropBehavior  behavior 
)
static

Definition at line 3908 of file tablecmds.c.

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

4221{
4222 Oid relid = InvalidOid;
4223 Oid typid = InvalidOid;
4224
4225 if (stmt->renameType == OBJECT_DOMCONSTRAINT)
4226 {
4227 Relation rel;
4228 HeapTuple tup;
4229
4233 if (!HeapTupleIsValid(tup))
4234 elog(ERROR, "cache lookup failed for type %u", typid);
4237 table_close(rel, NoLock);
4238 }
4239 else
4240 {
4241 /* lock level taken here should match rename_constraint_internal */
4243 stmt->missing_ok ? RVR_MISSING_OK : 0,
4245 NULL);
4246 if (!OidIsValid(relid))
4247 {
4249 (errmsg("relation \"%s\" does not exist, skipping",
4250 stmt->relation->relname)));
4251 return InvalidObjectAddress;
4252 }
4253 }
4254
4255 return
4256 rename_constraint_internal(relid, typid,
4257 stmt->subname,
4258 stmt->newname,
4259 (stmt->relation &&
4260 stmt->relation->inh), /* recursive? */
4261 false, /* recursing? */
4262 0 /* expected inhcount */ );
4263}
void checkDomainOwner(HeapTuple tup)
Definition typecmds.c:3526

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

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

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

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

4428{
4429 Relation relrelation; /* for RELATION relation */
4432
4433 /*
4434 * Find relation's pg_class tuple.
4435 */
4437
4439 if (!HeapTupleIsValid(reltup)) /* shouldn't happen */
4440 elog(ERROR, "cache lookup failed for relation %u", myrelid);
4442
4443 /*
4444 * Update pg_class tuple.
4445 */
4446 relform->relrewrite = InvalidOid;
4447
4449
4452}

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

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

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

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

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

3712{
3714 HeapTuple tuple;
3716
3718 ShareUpdateExclusiveLock, false) ||
3720 ShareRowExclusiveLock, true));
3721
3722 /*
3723 * Fetch a modifiable copy of the tuple, modify it, update pg_class.
3724 */
3727 if (!HeapTupleIsValid(tuple))
3728 elog(ERROR, "cache lookup failed for relation %u", relationId);
3730
3731 if (classtuple->relhassubclass != relhassubclass)
3732 {
3733 classtuple->relhassubclass = relhassubclass;
3735 }
3736 else
3737 {
3738 /* no need to change tuple, but force relcache rebuild anyway */
3740 }
3741
3742 heap_freetuple(tuple);
3744}
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 3814 of file tablecmds.c.

3817{
3819 HeapTuple tuple;
3821 Form_pg_class rd_rel;
3822 Oid reloid = RelationGetRelid(rel);
3823
3825
3826 /* Get a modifiable copy of the relation's pg_class row. */
3828
3830 if (!HeapTupleIsValid(tuple))
3831 elog(ERROR, "cache lookup failed for relation %u", reloid);
3832 otid = tuple->t_self;
3833 rd_rel = (Form_pg_class) GETSTRUCT(tuple);
3834
3835 /* Update the pg_class row. */
3836 rd_rel->reltablespace = (newTableSpaceId == MyDatabaseTableSpace) ?
3839 rd_rel->relfilenode = newRelFilenumber;
3842
3843 /*
3844 * Record dependency on tablespace. This is only required for relations
3845 * that have no physical storage.
3846 */
3847 if (!RELKIND_HAS_STORAGE(rel->rd_rel->relkind))
3849 rd_rel->reltablespace);
3850
3851 heap_freetuple(tuple);
3853}
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 23773 of file tablecmds.c.

23775{
23776 /* The FSM is empty, so don't bother using it. */
23777 uint32 ti_options = TABLE_INSERT_SKIP_FSM;
23778 CommandId mycid;
23779 EState *estate;
23781 *listptr2;
23783 ExprContext *econtext;
23784 TableScanDesc scan;
23785 Snapshot snapshot;
23790 *pc;
23791
23792 mycid = GetCurrentCommandId(true);
23793
23794 estate = CreateExecutorState();
23795
23796 forboth(listptr, partlist, listptr2, newPartRels)
23797 {
23799
23801
23802 /* Find the work queue entry for the new partition table: newPartRel. */
23803 pc->tab = ATGetQueueEntry(wqueue, pc->partRel);
23804
23805 buildExpressionExecutionStates(pc->tab, pc->partRel, estate);
23806
23807 if (sps->bound->is_default)
23808 {
23809 /*
23810 * We should not create a structure to check the partition
23811 * constraint for the new DEFAULT partition.
23812 */
23814 }
23815 else
23816 {
23818
23819 /* Build expression execution states for partition check quals. */
23823 (Node *) partConstraint);
23824 /* Make a boolean expression for ExecCheck(). */
23826
23827 /*
23828 * Map the vars in the constraint expression from rel's attnos to
23829 * splitRel's.
23830 */
23832 1, splitRel, rel);
23833
23834 pc->partqualstate =
23836 Assert(pc->partqualstate != NULL);
23837 }
23838
23839 /* Store partition context into a list. */
23841 }
23842
23843 econtext = GetPerTupleExprContext(estate);
23844
23845 /* Create the necessary tuple slot. */
23847
23848 /*
23849 * Map computing for moving attributes of the split partition to the new
23850 * partition (for the first new partition, but other new partitions can
23851 * use the same map).
23852 */
23855 RelationGetDescr(pc->partRel));
23856
23857 /* Scan through the rows. */
23858 snapshot = RegisterSnapshot(GetLatestSnapshot());
23859 scan = table_beginscan(splitRel, snapshot, 0, NULL,
23860 SO_NONE);
23861
23862 /*
23863 * Switch to per-tuple memory context and reset it for each tuple
23864 * produced, so we don't leak memory.
23865 */
23867
23869 {
23870 bool found = false;
23872
23874
23875 econtext->ecxt_scantuple = srcslot;
23876
23877 /* Search partition for the current slot, srcslot. */
23878 foreach(listptr, partContexts)
23879 {
23881
23882 /* skip DEFAULT partition */
23883 if (pc->partqualstate && ExecCheck(pc->partqualstate, econtext))
23884 {
23885 found = true;
23886 break;
23887 }
23888 }
23889 if (!found)
23890 {
23891 /* Use the DEFAULT partition if it exists. */
23892 if (defaultPartCtx)
23894 else
23895 ereport(ERROR,
23897 errmsg("cannot find partition for split partition row"),
23899 }
23900
23901 if (tuple_map)
23902 {
23903 /* Need to use a map to copy attributes. */
23904 insertslot = execute_attr_map_slot(tuple_map->attrMap, srcslot, pc->dstslot);
23905 }
23906 else
23907 {
23908 /* Extract data from the old tuple. */
23910
23911 /* Copy attributes directly. */
23912 insertslot = pc->dstslot;
23913
23915
23916 memcpy(insertslot->tts_values, srcslot->tts_values,
23917 sizeof(Datum) * srcslot->tts_nvalid);
23918 memcpy(insertslot->tts_isnull, srcslot->tts_isnull,
23919 sizeof(bool) * srcslot->tts_nvalid);
23920
23922 }
23923
23924 /*
23925 * Constraints and GENERATED expressions might reference the tableoid
23926 * column, so fill tts_tableOid with the desired value. (We must do
23927 * this each time, because it gets overwritten with newrel's OID
23928 * during storing.)
23929 */
23930 insertslot->tts_tableOid = RelationGetRelid(pc->partRel);
23931
23932 /*
23933 * Now, evaluate any generated expressions whose inputs come from the
23934 * new tuple. We assume these columns won't reference each other, so
23935 * that there's no ordering dependency.
23936 */
23938 insertslot, econtext);
23939
23940 /* Write the tuple out to the new relation. */
23941 table_tuple_insert(pc->partRel, insertslot, mycid,
23942 ti_options, pc->bistate);
23943
23944 ResetExprContext(econtext);
23945 }
23946
23948
23949 table_endscan(scan);
23950 UnregisterSnapshot(snapshot);
23951
23952 if (tuple_map)
23954
23956
23957 FreeExecutorState(estate);
23958
23961}
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 2526 of file tablecmds.c.

2527{
2528 switch (c)
2529 {
2530 case TYPSTORAGE_PLAIN:
2531 return "PLAIN";
2533 return "EXTERNAL";
2535 return "EXTENDED";
2536 case TYPSTORAGE_MAIN:
2537 return "MAIN";
2538 default:
2539 return "???";
2540 }
2541}
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 3585 of file tablecmds.c.

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

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

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

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

13803{
13804 ListCell *l;
13805 int attnum;
13806
13807 attnum = 0;
13808 foreach(l, colList)
13809 {
13810 char *attname = strVal(lfirst(l));
13813
13816 ereport(ERROR,
13818 errmsg("column \"%s\" referenced in foreign key constraint does not exist",
13819 attname)));
13821 if (attform->attnum < 0)
13822 ereport(ERROR,
13824 errmsg("system columns cannot be used in foreign keys")));
13825 if (attnum >= INDEX_MAX_KEYS)
13826 ereport(ERROR,
13828 errmsg("cannot have more than %d keys in a foreign key",
13829 INDEX_MAX_KEYS)));
13830 attnums[attnum] = attform->attnum;
13831 if (atttypids != NULL)
13832 atttypids[attnum] = attform->atttypid;
13833 if (attcollids != NULL)
13834 attcollids[attnum] = attform->attcollation;
13836 attnum++;
13837 }
13838
13839 return attnum;
13840}

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

13963{
13964 Oid indexoid = InvalidOid;
13965 bool found = false;
13966 bool found_deferrable = false;
13969 int i,
13970 j;
13971
13972 /*
13973 * Reject duplicate appearances of columns in the referenced-columns list.
13974 * Such a case is forbidden by the SQL standard, and even if we thought it
13975 * useful to allow it, there would be ambiguity about how to match the
13976 * list to unique indexes (in particular, it'd be unclear which index
13977 * opclass goes with which FK column).
13978 */
13979 for (i = 0; i < numattrs; i++)
13980 {
13981 for (j = i + 1; j < numattrs; j++)
13982 {
13983 if (attnums[i] == attnums[j])
13984 ereport(ERROR,
13986 errmsg("foreign key referenced-columns list must not contain duplicates")));
13987 }
13988 }
13989
13990 /*
13991 * Get the list of index OIDs for the table from the relcache, and look up
13992 * each one in the pg_index syscache, and match unique indexes to the list
13993 * of attnums we are given.
13994 */
13996
13997 foreach(indexoidscan, indexoidlist)
13998 {
14001
14002 indexoid = lfirst_oid(indexoidscan);
14005 elog(ERROR, "cache lookup failed for index %u", indexoid);
14007
14008 /*
14009 * Must have the right number of columns; must be unique (or if
14010 * temporal then exclusion instead) and not a partial index; forget it
14011 * if there are any expressions, too. Invalid indexes are out as well.
14012 */
14013 if (indexStruct->indnkeyatts == numattrs &&
14014 (with_period ? indexStruct->indisexclusion : indexStruct->indisunique) &&
14018 {
14021
14022 /* Must get indclass the hard way */
14026
14027 /*
14028 * The given attnum list may match the index columns in any order.
14029 * Check for a match, and extract the appropriate opclasses while
14030 * we're at it.
14031 *
14032 * We know that attnums[] is duplicate-free per the test at the
14033 * start of this function, and we checked above that the number of
14034 * index columns agrees, so if we find a match for each attnums[]
14035 * entry then we must have a one-to-one match in some order.
14036 */
14037 for (i = 0; i < numattrs; i++)
14038 {
14039 found = false;
14040 for (j = 0; j < numattrs; j++)
14041 {
14042 if (attnums[i] == indexStruct->indkey.values[j])
14043 {
14044 opclasses[i] = indclass->values[j];
14045 found = true;
14046 break;
14047 }
14048 }
14049 if (!found)
14050 break;
14051 }
14052 /* The last attribute in the index must be the PERIOD FK part */
14053 if (found && with_period)
14054 {
14055 int16 periodattnum = attnums[numattrs - 1];
14056
14057 found = (periodattnum == indexStruct->indkey.values[numattrs - 1]);
14058 }
14059
14060 /*
14061 * Refuse to use a deferrable unique/primary key. This is per SQL
14062 * spec, and there would be a lot of interesting semantic problems
14063 * if we tried to allow it.
14064 */
14065 if (found && !indexStruct->indimmediate)
14066 {
14067 /*
14068 * Remember that we found an otherwise matching index, so that
14069 * we can generate a more appropriate error message.
14070 */
14071 found_deferrable = true;
14072 found = false;
14073 }
14074
14075 /* We need to know whether the index has WITHOUT OVERLAPS */
14076 if (found)
14077 *pk_has_without_overlaps = indexStruct->indisexclusion;
14078 }
14080 if (found)
14081 break;
14082 }
14083
14084 if (!found)
14085 {
14086 if (found_deferrable)
14087 ereport(ERROR,
14089 errmsg("cannot use a deferrable unique constraint for referenced table \"%s\"",
14091 else
14092 ereport(ERROR,
14094 errmsg("there is no unique constraint matching given keys for referenced table \"%s\"",
14096 }
14097
14099
14100 return indexoid;
14101}
bool heap_attisnull(HeapTuple tup, int attnum, TupleDesc tupleDesc)
Definition heaptuple.c:456
Definition c.h:871

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

13860{
13867 int i;
13868
13869 /*
13870 * Get the list of index OIDs for the table from the relcache, and look up
13871 * each one in the pg_index syscache until we find one marked primary key
13872 * (hopefully there isn't more than one such). Insist it's valid, too.
13873 */
13874 *indexOid = InvalidOid;
13875
13877
13878 foreach(indexoidscan, indexoidlist)
13879 {
13880 Oid indexoid = lfirst_oid(indexoidscan);
13881
13884 elog(ERROR, "cache lookup failed for index %u", indexoid);
13886 if (indexStruct->indisprimary && indexStruct->indisvalid)
13887 {
13888 /*
13889 * Refuse to use a deferrable primary key. This is per SQL spec,
13890 * and there would be a lot of interesting semantic problems if we
13891 * tried to allow it.
13892 */
13893 if (!indexStruct->indimmediate)
13894 ereport(ERROR,
13896 errmsg("cannot use a deferrable primary key for referenced table \"%s\"",
13898
13899 *indexOid = indexoid;
13900 break;
13901 }
13903 }
13904
13906
13907 /*
13908 * Check that we found it
13909 */
13910 if (!OidIsValid(*indexOid))
13911 ereport(ERROR,
13913 errmsg("there is no primary key for referenced table \"%s\"",
13915
13916 /* Must get indclass the hard way */
13920
13921 /*
13922 * Now build the list of PK attributes from the indkey definition (we
13923 * assume a primary key cannot have expressional elements)
13924 */
13925 *attnamelist = NIL;
13926 for (i = 0; i < indexStruct->indnkeyatts; i++)
13927 {
13928 int pkattno = indexStruct->indkey.values[i];
13929
13930 attnums[i] = pkattno;
13933 opclasses[i] = indclass->values[i];
13936 }
13937
13938 *pk_has_without_overlaps = indexStruct->indisexclusion;
13939
13941
13942 return i;
13943}
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 20198 of file tablecmds.c.

20199{
20201 ParseState *pstate;
20203 ListCell *l;
20204
20206
20207 newspec->strategy = partspec->strategy;
20208 newspec->partParams = NIL;
20209 newspec->location = partspec->location;
20210
20211 /* Check valid number of columns for strategy */
20212 if (partspec->strategy == PARTITION_STRATEGY_LIST &&
20213 list_length(partspec->partParams) != 1)
20214 ereport(ERROR,
20216 errmsg("cannot use \"list\" partition strategy with more than one column")));
20217
20218 /*
20219 * Create a dummy ParseState and insert the target relation as its sole
20220 * rangetable entry. We need a ParseState for transformExpr.
20221 */
20222 pstate = make_parsestate(NULL);
20224 NULL, false, true);
20225 addNSItemToQuery(pstate, nsitem, true, true, true);
20226
20227 /* take care of any partition expressions */
20228 foreach(l, partspec->partParams)
20229 {
20231
20232 if (pelem->expr)
20233 {
20234 /* Copy, to avoid scribbling on the input */
20236
20237 /* Now do parse transformation of the expression */
20238 pelem->expr = transformExpr(pstate, pelem->expr,
20240
20241 /* we have to fix its collations too */
20242 assign_expr_collations(pstate, pelem->expr);
20243 }
20244
20245 newspec->partParams = lappend(newspec->partParams, pelem);
20246 }
20247
20248 return newspec;
20249}
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 2503 of file tablecmds.c.

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

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

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

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

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

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

◆ tryAttachPartitionForeignKey()

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

Definition at line 11779 of file tablecmds.c.

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

16390{
16391 HeapTuple tup;
16392 Datum adatum;
16393 ArrayType *arr;
16394 Oid *rawarr;
16395 int numkeys;
16396 int i;
16397
16398 Assert(con->contype == CONSTR_FOREIGN);
16399 Assert(con->old_conpfeqop == NIL); /* already prepared this node */
16400
16402 if (!HeapTupleIsValid(tup)) /* should not happen */
16403 elog(ERROR, "cache lookup failed for constraint %u", oldId);
16404
16407 arr = DatumGetArrayTypeP(adatum); /* ensure not toasted */
16408 numkeys = ARR_DIMS(arr)[0];
16409 /* test follows the one in ri_FetchConstraintInfo() */
16410 if (ARR_NDIM(arr) != 1 ||
16411 ARR_HASNULL(arr) ||
16412 ARR_ELEMTYPE(arr) != OIDOID)
16413 elog(ERROR, "conpfeqop is not a 1-D Oid array");
16414 rawarr = (Oid *) ARR_DATA_PTR(arr);
16415
16416 /* stash a List of the operator Oids in our Constraint node */
16417 for (i = 0; i < numkeys; i++)
16419
16421}
#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 16360 of file tablecmds.c.

16361{
16363 stmt->accessMethod,
16364 stmt->indexParams,
16365 stmt->excludeOpNames,
16366 stmt->iswithoutoverlaps))
16367 {
16369
16370 /* If it's a partitioned index, there is no storage to share. */
16371 if (irel->rd_rel->relkind != RELKIND_PARTITIONED_INDEX)
16372 {
16373 stmt->oldNumber = irel->rd_locator.relNumber;
16374 stmt->oldCreateSubid = irel->rd_createSubid;
16375 stmt->oldFirstRelfilelocatorSubid = irel->rd_firstRelfilelocatorSubid;
16376 }
16377 index_close(irel, NoLock);
16378 }
16379}
bool CheckIndexCompatible(Oid oldId, const char *accessMethodName, const List *attributeList, const List *exclusionOpNames, bool isWithoutOverlaps)
Definition indexcmds.c:179

References CheckIndexCompatible(), fb(), index_close(), index_open(), NoLock, RelationData::rd_createSubid, RelationData::rd_firstRelfilelocatorSubid, RelationData::rd_locator, RelationData::rd_rel, RelFileLocator::relNumber, and stmt.

Referenced by ATPostAlterTypeParse().

◆ validateFkOnDeleteSetColumns()

static int validateFkOnDeleteSetColumns ( int  numfks,
const int16 fkattnums,
int  numfksetcols,
int16 fksetcolsattnums,
List fksetcols 
)
static

Definition at line 10731 of file tablecmds.c.

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

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

14174{
14175 TupleTableSlot *slot;
14176 TableScanDesc scan;
14177 Trigger trig = {0};
14178 Snapshot snapshot;
14181
14183 (errmsg_internal("validating foreign key constraint \"%s\"", conname)));
14184
14185 /*
14186 * Build a trigger call structure; we'll need it either way.
14187 */
14188 trig.tgoid = InvalidOid;
14189 trig.tgname = conname;
14190 trig.tgenabled = TRIGGER_FIRES_ON_ORIGIN;
14191 trig.tgisinternal = true;
14192 trig.tgconstrrelid = RelationGetRelid(pkrel);
14193 trig.tgconstrindid = pkindOid;
14194 trig.tgconstraint = constraintOid;
14195 trig.tgdeferrable = false;
14196 trig.tginitdeferred = false;
14197 /* we needn't fill in remaining fields */
14198
14199 /*
14200 * See if we can do it with a single LEFT JOIN query. A false result
14201 * indicates we must proceed with the fire-the-trigger method. We can't do
14202 * a LEFT JOIN for temporal FKs yet, but we can once we support temporal
14203 * left joins.
14204 */
14205 if (!hasperiod && RI_Initial_Check(&trig, rel, pkrel))
14206 return;
14207
14208 /*
14209 * Scan through each tuple, calling RI_FKey_check_ins (insert trigger) as
14210 * if that tuple had just been inserted. If any of those fail, it should
14211 * ereport(ERROR) and that's that.
14212 */
14213 snapshot = RegisterSnapshot(GetLatestSnapshot());
14214 slot = table_slot_create(rel, NULL);
14215 scan = table_beginscan(rel, snapshot, 0, NULL,
14216 SO_NONE);
14218 "validateForeignKeyConstraint",
14221
14222 while (table_scan_getnextslot(scan, ForwardScanDirection, slot))
14223 {
14224 LOCAL_FCINFO(fcinfo, 0);
14225 TriggerData trigdata = {0};
14226
14228
14229 /*
14230 * Make a call to the trigger function
14231 *
14232 * No parameters are passed, but we do set a context
14233 */
14234 MemSet(fcinfo, 0, SizeForFunctionCallInfo(0));
14235
14236 /*
14237 * We assume RI_FKey_check_ins won't look at flinfo...
14238 */
14239 trigdata.type = T_TriggerData;
14241 trigdata.tg_relation = rel;
14242 trigdata.tg_trigtuple = ExecFetchSlotHeapTuple(slot, false, NULL);
14243 trigdata.tg_trigslot = slot;
14244 trigdata.tg_trigger = &trig;
14245
14246 fcinfo->context = (Node *) &trigdata;
14247
14248 RI_FKey_check_ins(fcinfo);
14249
14251 }
14252
14255 table_endscan(scan);
14256 UnregisterSnapshot(snapshot);
14258}
#define MemSet(start, val, len)
Definition c.h:1163
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 22303 of file tablecmds.c.

22304{
22306 SysScanDesc scan;
22308 int tuples = 0;
22310 bool updated = false;
22311
22312 Assert(partedIdx->rd_rel->relkind == RELKIND_PARTITIONED_INDEX);
22313
22314 /*
22315 * Scan pg_inherits for this parent index. Count each valid index we find
22316 * (verifying the pg_index entry for each), and if we reach the total
22317 * amount we expect, we can mark this parent index as valid.
22318 */
22324 NULL, 1, &key);
22325 while ((inhTup = systable_getnext(scan)) != NULL)
22326 {
22330
22332 ObjectIdGetDatum(inhForm->inhrelid));
22334 elog(ERROR, "cache lookup failed for index %u", inhForm->inhrelid);
22336 if (indexForm->indisvalid)
22337 tuples += 1;
22339 }
22340
22341 /* Done with pg_inherits */
22342 systable_endscan(scan);
22344
22345 /*
22346 * If we found as many inherited indexes as the partitioned table has
22347 * partitions, we're good; update pg_index to set indisvalid.
22348 */
22349 if (tuples == RelationGetPartitionDesc(partedTbl, true)->nparts)
22350 {
22354
22359 elog(ERROR, "cache lookup failed for index %u",
22362
22363 indexForm->indisvalid = true;
22364 updated = true;
22365
22367
22370 }
22371
22372 /*
22373 * If this index is in turn a partition of a larger index, validating it
22374 * might cause the parent to become valid also. Try that.
22375 */
22376 if (updated && partedIdx->rd_rel->relispartition)
22377 {
22380 Relation parentIdx,
22381 parentTbl;
22382
22383 /* make sure we see the validation we just did */
22385
22390 Assert(!parentIdx->rd_index->indisvalid);
22391
22392 validatePartitionedIndex(parentIdx, parentTbl);
22393
22396 }
22397}

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

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

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

22406{
22407 for (int i = 0; i < iinfo->ii_NumIndexKeyAttrs; i++)
22408 {
22410 iinfo->ii_IndexAttrNumbers[i] - 1);
22411
22412 if (!att->attnotnull)
22413 ereport(ERROR,
22415 errmsg("invalid primary key definition"),
22416 errdetail("Column \"%s\" of relation \"%s\" is not marked NOT NULL.",
22417 NameStr(att->attname),
22419 }
22420}

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

Referenced by DropErrorMsgNonExistent(), and DropErrorMsgWrongType().

◆ on_commits