#include "access/htup.h"
#include "catalog/dependency.h"
#include "nodes/parsenodes.h"
#include "storage/lock.h"
#include "utils/relcache.h"
Go to the source code of this file.
void AlterRelationNamespaceInternal | ( | Relation | classRel, | |
Oid | relOid, | |||
Oid | oldNspOid, | |||
Oid | newNspOid, | |||
bool | hasDependEntry, | |||
ObjectAddresses * | objsMoved | |||
) |
Definition at line 9985 of file tablecmds.c.
References add_exact_object_address(), Assert, CatalogUpdateIndexes(), changeDependencyFor(), ObjectAddress::classId, elog, ereport, errcode(), errmsg(), ERROR, get_namespace_name(), get_relname_relid(), GETSTRUCT, heap_freetuple(), HeapTupleIsValid, InvalidOid, InvokeObjectPostAlterHook, NamespaceRelationId, NameStr, object_address_present(), ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, RelationRelationId, RELOID, SearchSysCacheCopy1, simple_heap_update(), and HeapTupleData::t_self.
Referenced by AlterIndexNamespaces(), AlterSeqNamespaces(), AlterTableNamespaceInternal(), and AlterTypeNamespaceInternal().
{ HeapTuple classTup; Form_pg_class classForm; ObjectAddress thisobj; classTup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relOid)); if (!HeapTupleIsValid(classTup)) elog(ERROR, "cache lookup failed for relation %u", relOid); classForm = (Form_pg_class) GETSTRUCT(classTup); Assert(classForm->relnamespace == oldNspOid); thisobj.classId = RelationRelationId; thisobj.objectId = relOid; thisobj.objectSubId = 0; /* * Do nothing when there's nothing to do. */ if (!object_address_present(&thisobj, objsMoved)) { /* check for duplicate name (more friendly than unique-index failure) */ if (get_relname_relid(NameStr(classForm->relname), newNspOid) != InvalidOid) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE), errmsg("relation \"%s\" already exists in schema \"%s\"", NameStr(classForm->relname), get_namespace_name(newNspOid)))); /* classTup is a copy, so OK to scribble on */ classForm->relnamespace = newNspOid; simple_heap_update(classRel, &classTup->t_self, classTup); CatalogUpdateIndexes(classRel, classTup); /* Update dependency on schema if caller said so */ if (hasDependEntry && changeDependencyFor(RelationRelationId, relOid, NamespaceRelationId, oldNspOid, newNspOid) != 1) elog(ERROR, "failed to change schema dependency for relation \"%s\"", NameStr(classForm->relname)); add_exact_object_address(&thisobj, objsMoved); InvokeObjectPostAlterHook(RelationRelationId, relOid, 0); } heap_freetuple(classTup); }
void AlterTable | ( | Oid | relid, | |
LOCKMODE | lockmode, | |||
AlterTableStmt * | stmt | |||
) |
Definition at line 2670 of file tablecmds.c.
References ATController(), CheckTableNotInUse(), AlterTableStmt::cmds, RangeVar::inhOpt, interpretInhOption(), NoLock, AlterTableStmt::relation, and relation_open().
Referenced by ProcessUtilitySlow().
{ Relation rel; /* Caller is required to provide an adequate lock. */ rel = relation_open(relid, NoLock); CheckTableNotInUse(rel, "ALTER TABLE"); ATController(rel, stmt->cmds, interpretInhOption(stmt->relation->inhOpt), lockmode); }
Definition at line 2727 of file tablecmds.c.
References AT_AddColumn, AT_AddColumnToView, AT_AddConstraint, AT_AddConstraintRecurse, AT_AddIndex, AT_AddIndexConstraint, AT_AddInherit, AT_AddOf, AT_AddOids, AT_AlterColumnGenericOptions, AT_AlterColumnType, AT_ChangeOwner, AT_ClusterOn, AT_ColumnDefault, AT_DisableRule, AT_DisableTrig, AT_DisableTrigAll, AT_DisableTrigUser, AT_DropCluster, AT_DropColumn, AT_DropConstraint, AT_DropInherit, AT_DropNotNull, AT_DropOf, AT_DropOids, AT_EnableAlwaysRule, AT_EnableAlwaysTrig, AT_EnableReplicaRule, AT_EnableReplicaTrig, AT_EnableRule, AT_EnableTrig, AT_EnableTrigAll, AT_EnableTrigUser, AT_GenericOptions, AT_ProcessedConstraint, AT_ReAddConstraint, AT_ReplaceRelOptions, AT_ResetOptions, AT_ResetRelOptions, AT_SetNotNull, AT_SetOptions, AT_SetRelOptions, AT_SetStatistics, AT_SetStorage, AT_SetTableSpace, AT_ValidateConstraint, CONSTR_EXCLUSION, CONSTR_FOREIGN, CONSTR_PRIMARY, CONSTR_UNIQUE, Constraint::contype, AlterTableCmd::def, elog, ERROR, IsA, lfirst, and AlterTableCmd::subtype.
Referenced by AlterTableInternal(), ProcessUtilitySlow(), and transformAlterTableStmt().
{ /* * Late in 9.1 dev cycle a number of issues were uncovered with access to * catalog relations, leading to the decision to re-enforce all DDL at * AccessExclusiveLock level by default. * * The issues are that there is a pervasive assumption in the code that * the catalogs will not be read unless an AccessExclusiveLock is held. If * that rule is relaxed, we must protect against a number of potential * effects - infrequent, but proven possible with test cases where * multiple DDL operations occur in a stream against frequently accessed * tables. * * 1. Catalog tables are read using SnapshotNow, which has a race bug that * allows a scan to return no valid rows even when one is present in the * case of a commit of a concurrent update of the catalog table. * SnapshotNow also ignores transactions in progress, so takes the latest * committed version without waiting for the latest changes. * * 2. Relcache needs to be internally consistent, so unless we lock the * definition during reads we have no way to guarantee that. * * 3. Catcache access isn't coordinated at all so refreshes can occur at * any time. */ #ifdef REDUCED_ALTER_TABLE_LOCK_LEVELS ListCell *lcmd; LOCKMODE lockmode = ShareUpdateExclusiveLock; foreach(lcmd, cmds) { AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd); LOCKMODE cmd_lockmode = AccessExclusiveLock; /* default for compiler */ switch (cmd->subtype) { /* * Need AccessExclusiveLock for these subcommands because they * affect or potentially affect both read and write * operations. * * New subcommand types should be added here by default. */ case AT_AddColumn: /* may rewrite heap, in some cases and visible * to SELECT */ case AT_DropColumn: /* change visible to SELECT */ case AT_AddColumnToView: /* CREATE VIEW */ case AT_AlterColumnType: /* must rewrite heap */ case AT_DropConstraint: /* as DROP INDEX */ case AT_AddOids: /* must rewrite heap */ case AT_DropOids: /* calls AT_DropColumn */ case AT_EnableAlwaysRule: /* may change SELECT rules */ case AT_EnableReplicaRule: /* may change SELECT rules */ case AT_EnableRule: /* may change SELECT rules */ case AT_DisableRule: /* may change SELECT rules */ case AT_ChangeOwner: /* change visible to SELECT */ case AT_SetTableSpace: /* must rewrite heap */ case AT_DropNotNull: /* may change some SQL plans */ case AT_SetNotNull: case AT_GenericOptions: case AT_AlterColumnGenericOptions: cmd_lockmode = AccessExclusiveLock; break; /* * These subcommands affect write operations only. */ case AT_ColumnDefault: case AT_ProcessedConstraint: /* becomes AT_AddConstraint */ case AT_AddConstraintRecurse: /* becomes AT_AddConstraint */ case AT_ReAddConstraint: /* becomes AT_AddConstraint */ case AT_EnableTrig: case AT_EnableAlwaysTrig: case AT_EnableReplicaTrig: case AT_EnableTrigAll: case AT_EnableTrigUser: case AT_DisableTrig: case AT_DisableTrigAll: case AT_DisableTrigUser: case AT_AddIndex: /* from ADD CONSTRAINT */ case AT_AddIndexConstraint: cmd_lockmode = ShareRowExclusiveLock; break; case AT_AddConstraint: if (IsA(cmd->def, Constraint)) { Constraint *con = (Constraint *) cmd->def; switch (con->contype) { case CONSTR_EXCLUSION: case CONSTR_PRIMARY: case CONSTR_UNIQUE: /* * Cases essentially the same as CREATE INDEX. We * could reduce the lock strength to ShareLock if * we can work out how to allow concurrent catalog * updates. */ cmd_lockmode = ShareRowExclusiveLock; break; case CONSTR_FOREIGN: /* * We add triggers to both tables when we add a * Foreign Key, so the lock level must be at least * as strong as CREATE TRIGGER. */ cmd_lockmode = ShareRowExclusiveLock; break; default: cmd_lockmode = ShareRowExclusiveLock; } } break; /* * These subcommands affect inheritance behaviour. Queries * started before us will continue to see the old inheritance * behaviour, while queries started after we commit will see * new behaviour. No need to prevent reads or writes to the * subtable while we hook it up though. */ case AT_AddInherit: case AT_DropInherit: cmd_lockmode = ShareUpdateExclusiveLock; break; /* * These subcommands affect implicit row type conversion. They * have affects similar to CREATE/DROP CAST on queries. We * don't provide for invalidating parse trees as a result of * such changes. Do avoid concurrent pg_class updates, * though. */ case AT_AddOf: case AT_DropOf: cmd_lockmode = ShareUpdateExclusiveLock; /* * These subcommands affect general strategies for performance * and maintenance, though don't change the semantic results * from normal data reads and writes. Delaying an ALTER TABLE * behind currently active writes only delays the point where * the new strategy begins to take effect, so there is no * benefit in waiting. In this case the minimum restriction * applies: we don't currently allow concurrent catalog * updates. */ case AT_SetStatistics: case AT_ClusterOn: case AT_DropCluster: case AT_SetRelOptions: case AT_ResetRelOptions: case AT_ReplaceRelOptions: case AT_SetOptions: case AT_ResetOptions: case AT_SetStorage: case AT_ValidateConstraint: cmd_lockmode = ShareUpdateExclusiveLock; break; default: /* oops */ elog(ERROR, "unrecognized alter table type: %d", (int) cmd->subtype); break; } /* * Take the greatest lockmode from any subcommand */ if (cmd_lockmode > lockmode) lockmode = cmd_lockmode; } #else LOCKMODE lockmode = AccessExclusiveLock; #endif return lockmode; }
Definition at line 2695 of file tablecmds.c.
References AlterTableGetLockLevel(), ATController(), and relation_open().
Referenced by DefineVirtualRelation(), and index_check_primary_key().
{ Relation rel; LOCKMODE lockmode = AlterTableGetLockLevel(cmds); rel = relation_open(relid, lockmode); ATController(rel, cmds, recurse, lockmode); }
Oid AlterTableLookupRelation | ( | AlterTableStmt * | stmt, | |
LOCKMODE | lockmode | |||
) |
Definition at line 2618 of file tablecmds.c.
References AlterTableStmt::missing_ok, RangeVarCallbackForAlterRelation(), RangeVarGetRelidExtended(), and AlterTableStmt::relation.
Referenced by ProcessUtilitySlow().
{ return RangeVarGetRelidExtended(stmt->relation, lockmode, stmt->missing_ok, false, RangeVarCallbackForAlterRelation, (void *) stmt); }
Oid AlterTableNamespace | ( | AlterObjectSchemaStmt * | stmt | ) |
Definition at line 9884 of file tablecmds.c.
References AccessExclusiveLock, AlterTableNamespaceInternal(), CheckSetNamespace(), ereport, errcode(), errdetail(), errmsg(), ERROR, free_object_addresses(), get_rel_name(), makeRangeVar(), AlterObjectSchemaStmt::missing_ok, new_object_addresses(), NoLock, NOTICE, NULL, OidIsValid, RangeVarCallbackForAlterRelation(), RangeVarGetAndCheckCreationNamespace(), RangeVarGetRelidExtended(), RelationData::rd_rel, AlterObjectSchemaStmt::relation, relation_close(), relation_open(), RelationGetNamespace, RelationGetRelationName, RelationRelationId, RELKIND_SEQUENCE, and sequenceIsOwned().
Referenced by ExecAlterObjectSchemaStmt().
{ Relation rel; Oid relid; Oid oldNspOid; Oid nspOid; RangeVar *newrv; ObjectAddresses *objsMoved; relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, stmt->missing_ok, false, RangeVarCallbackForAlterRelation, (void *) stmt); if (!OidIsValid(relid)) { ereport(NOTICE, (errmsg("relation \"%s\" does not exist, skipping", stmt->relation->relname))); return InvalidOid; } rel = relation_open(relid, NoLock); oldNspOid = RelationGetNamespace(rel); /* If it's an owned sequence, disallow moving it by itself. */ if (rel->rd_rel->relkind == RELKIND_SEQUENCE) { Oid tableId; int32 colId; if (sequenceIsOwned(relid, &tableId, &colId)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot move an owned sequence into another schema"), errdetail("Sequence \"%s\" is linked to table \"%s\".", RelationGetRelationName(rel), get_rel_name(tableId)))); } /* Get and lock schema OID and check its permissions. */ newrv = makeRangeVar(stmt->newschema, RelationGetRelationName(rel), -1); nspOid = RangeVarGetAndCheckCreationNamespace(newrv, NoLock, NULL); /* common checks on switching namespaces */ CheckSetNamespace(oldNspOid, nspOid, RelationRelationId, relid); objsMoved = new_object_addresses(); AlterTableNamespaceInternal(rel, oldNspOid, nspOid, objsMoved); free_object_addresses(objsMoved); /* close rel, but keep lock until commit */ relation_close(rel, NoLock); return relid; }
void AlterTableNamespaceInternal | ( | Relation | rel, | |
Oid | oldNspOid, | |||
Oid | nspOid, | |||
ObjectAddresses * | objsMoved | |||
) |
Definition at line 9948 of file tablecmds.c.
References AccessExclusiveLock, AlterConstraintNamespaces(), AlterIndexNamespaces(), AlterRelationNamespaceInternal(), AlterSeqNamespaces(), AlterTypeNamespaceInternal(), Assert, heap_close, heap_open(), NULL, RelationData::rd_rel, RelationGetRelid, RelationRelationId, RELKIND_MATVIEW, RELKIND_RELATION, and RowExclusiveLock.
Referenced by AlterObjectNamespace_oid(), and AlterTableNamespace().
{ Relation classRel; Assert(objsMoved != NULL); /* OK, modify the pg_class row and pg_depend entry */ classRel = heap_open(RelationRelationId, RowExclusiveLock); AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid, nspOid, true, objsMoved); /* Fix the table's row type too */ AlterTypeNamespaceInternal(rel->rd_rel->reltype, nspOid, false, false, objsMoved); /* Fix other dependent stuff */ if (rel->rd_rel->relkind == RELKIND_RELATION || rel->rd_rel->relkind == RELKIND_MATVIEW) { AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved); AlterSeqNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved, AccessExclusiveLock); AlterConstraintNamespaces(RelationGetRelid(rel), oldNspOid, nspOid, false, objsMoved); } heap_close(classRel, RowExclusiveLock); }
void AtEOSubXact_on_commit_actions | ( | bool | isCommit, | |
SubTransactionId | mySubid, | |||
SubTransactionId | parentSubid | |||
) |
Definition at line 10342 of file tablecmds.c.
References OnCommitItem::creating_subid, OnCommitItem::deleting_subid, lfirst, list_delete_cell(), list_head(), lnext, NULL, and pfree().
Referenced by AbortSubTransaction(), and CommitSubTransaction().
{ ListCell *cur_item; ListCell *prev_item; prev_item = NULL; cur_item = list_head(on_commits); while (cur_item != NULL) { OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item); if (!isCommit && oc->creating_subid == mySubid) { /* cur_item must be removed */ on_commits = list_delete_cell(on_commits, cur_item, prev_item); pfree(oc); if (prev_item) cur_item = lnext(prev_item); else cur_item = list_head(on_commits); } else { /* cur_item must be preserved */ if (oc->creating_subid == mySubid) oc->creating_subid = parentSubid; if (oc->deleting_subid == mySubid) oc->deleting_subid = isCommit ? parentSubid : InvalidSubTransactionId; prev_item = cur_item; cur_item = lnext(prev_item); } } }
void AtEOXact_on_commit_actions | ( | bool | isCommit | ) |
Definition at line 10300 of file tablecmds.c.
References OnCommitItem::creating_subid, OnCommitItem::deleting_subid, InvalidSubTransactionId, lfirst, list_delete_cell(), list_head(), lnext, NULL, and pfree().
Referenced by AbortTransaction(), CommitTransaction(), and PrepareTransaction().
{ ListCell *cur_item; ListCell *prev_item; prev_item = NULL; cur_item = list_head(on_commits); while (cur_item != NULL) { OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item); if (isCommit ? oc->deleting_subid != InvalidSubTransactionId : oc->creating_subid != InvalidSubTransactionId) { /* cur_item must be removed */ on_commits = list_delete_cell(on_commits, cur_item, prev_item); pfree(oc); if (prev_item) cur_item = lnext(prev_item); else cur_item = list_head(on_commits); } else { /* cur_item must be preserved */ oc->creating_subid = InvalidSubTransactionId; oc->deleting_subid = InvalidSubTransactionId; prev_item = cur_item; cur_item = lnext(prev_item); } } }
Definition at line 8134 of file tablecmds.c.
References ACL_CREATE, ACL_KIND_CLASS, ACL_KIND_NAMESPACE, aclcheck_error(), ACLCHECK_NOT_OWNER, ACLCHECK_OK, aclnewowner(), AlterTypeOwnerInternal(), Anum_pg_class_relacl, Anum_pg_class_relowner, ATExecChangeOwner(), CatalogUpdateIndexes(), change_owner_fix_column_acls(), change_owner_recurse_to_sequences(), changeDependencyOnOwner(), check_is_member_of_role(), DatumGetAclP, elog, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, get_namespace_name(), get_rel_name(), GETSTRUCT, GetUserId(), heap_close, heap_freetuple(), heap_modify_tuple(), heap_open(), HeapTupleIsValid, i, InvalidOid, InvokeObjectPostAlterHook, lfirst_oid, list_free(), NameStr, NoLock, ObjectIdGetDatum, pg_class_ownercheck(), pg_namespace_aclcheck(), PointerGetDatum, relation_close(), relation_open(), RelationGetDescr, RelationGetIndexList(), RelationGetRelationName, RelationRelationId, ReleaseSysCache(), RELKIND_COMPOSITE_TYPE, RELKIND_FOREIGN_TABLE, RELKIND_INDEX, RELKIND_MATVIEW, RELKIND_RELATION, RELKIND_SEQUENCE, RELKIND_TOASTVALUE, RELKIND_VIEW, RELOID, RowExclusiveLock, SearchSysCache1, sequenceIsOwned(), simple_heap_update(), superuser(), SysCacheGetAttr(), HeapTupleData::t_self, and WARNING.
Referenced by AlterTypeOwner(), ATExecChangeOwner(), ATExecCmd(), change_owner_recurse_to_sequences(), and shdepReassignOwned().
{ Relation target_rel; Relation class_rel; HeapTuple tuple; Form_pg_class tuple_class; /* * Get exclusive lock till end of transaction on the target table. Use * relation_open so that we can work on indexes and sequences. */ target_rel = relation_open(relationOid, lockmode); /* Get its pg_class tuple, too */ class_rel = heap_open(RelationRelationId, RowExclusiveLock); tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relationOid)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", relationOid); tuple_class = (Form_pg_class) GETSTRUCT(tuple); /* Can we change the ownership of this tuple? */ switch (tuple_class->relkind) { case RELKIND_RELATION: case RELKIND_VIEW: case RELKIND_MATVIEW: case RELKIND_FOREIGN_TABLE: /* ok to change owner */ break; case RELKIND_INDEX: if (!recursing) { /* * Because ALTER INDEX OWNER used to be allowed, and in fact * is generated by old versions of pg_dump, we give a warning * and do nothing rather than erroring out. Also, to avoid * unnecessary chatter while restoring those old dumps, say * nothing at all if the command would be a no-op anyway. */ if (tuple_class->relowner != newOwnerId) ereport(WARNING, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot change owner of index \"%s\"", NameStr(tuple_class->relname)), errhint("Change the ownership of the index's table, instead."))); /* quick hack to exit via the no-op path */ newOwnerId = tuple_class->relowner; } break; case RELKIND_SEQUENCE: if (!recursing && tuple_class->relowner != newOwnerId) { /* if it's an owned sequence, disallow changing it by itself */ Oid tableId; int32 colId; if (sequenceIsOwned(relationOid, &tableId, &colId)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot change owner of sequence \"%s\"", NameStr(tuple_class->relname)), errdetail("Sequence \"%s\" is linked to table \"%s\".", NameStr(tuple_class->relname), get_rel_name(tableId)))); } break; case RELKIND_COMPOSITE_TYPE: if (recursing) break; ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is a composite type", NameStr(tuple_class->relname)), errhint("Use ALTER TYPE instead."))); break; case RELKIND_TOASTVALUE: if (recursing) break; /* FALL THRU */ default: ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table, view, sequence, or foreign table", NameStr(tuple_class->relname)))); } /* * If the new owner is the same as the existing owner, consider the * command to have succeeded. This is for dump restoration purposes. */ if (tuple_class->relowner != newOwnerId) { Datum repl_val[Natts_pg_class]; bool repl_null[Natts_pg_class]; bool repl_repl[Natts_pg_class]; Acl *newAcl; Datum aclDatum; bool isNull; HeapTuple newtuple; /* skip permission checks when recursing to index or toast table */ if (!recursing) { /* Superusers can always do it */ if (!superuser()) { Oid namespaceOid = tuple_class->relnamespace; AclResult aclresult; /* Otherwise, must be owner of the existing object */ if (!pg_class_ownercheck(relationOid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(target_rel)); /* Must be able to become new owner */ check_is_member_of_role(GetUserId(), newOwnerId); /* New owner must have CREATE privilege on namespace */ aclresult = pg_namespace_aclcheck(namespaceOid, newOwnerId, ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceOid)); } } memset(repl_null, false, sizeof(repl_null)); memset(repl_repl, false, sizeof(repl_repl)); repl_repl[Anum_pg_class_relowner - 1] = true; repl_val[Anum_pg_class_relowner - 1] = ObjectIdGetDatum(newOwnerId); /* * Determine the modified ACL for the new owner. This is only * necessary when the ACL is non-null. */ aclDatum = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_relacl, &isNull); if (!isNull) { newAcl = aclnewowner(DatumGetAclP(aclDatum), tuple_class->relowner, newOwnerId); repl_repl[Anum_pg_class_relacl - 1] = true; repl_val[Anum_pg_class_relacl - 1] = PointerGetDatum(newAcl); } newtuple = heap_modify_tuple(tuple, RelationGetDescr(class_rel), repl_val, repl_null, repl_repl); simple_heap_update(class_rel, &newtuple->t_self, newtuple); CatalogUpdateIndexes(class_rel, newtuple); heap_freetuple(newtuple); /* * We must similarly update any per-column ACLs to reflect the new * owner; for neatness reasons that's split out as a subroutine. */ change_owner_fix_column_acls(relationOid, tuple_class->relowner, newOwnerId); /* * Update owner dependency reference, if any. A composite type has * none, because it's tracked for the pg_type entry instead of here; * indexes and TOAST tables don't have their own entries either. */ if (tuple_class->relkind != RELKIND_COMPOSITE_TYPE && tuple_class->relkind != RELKIND_INDEX && tuple_class->relkind != RELKIND_TOASTVALUE) changeDependencyOnOwner(RelationRelationId, relationOid, newOwnerId); /* * Also change the ownership of the table's row type, if it has one */ if (tuple_class->relkind != RELKIND_INDEX) AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId, tuple_class->relkind == RELKIND_COMPOSITE_TYPE); /* * If we are operating on a table or materialized view, also change * the ownership of any indexes and sequences that belong to the * relation, as well as its toast table (if it has one). */ if (tuple_class->relkind == RELKIND_RELATION || tuple_class->relkind == RELKIND_MATVIEW || tuple_class->relkind == RELKIND_TOASTVALUE) { List *index_oid_list; ListCell *i; /* Find all the indexes belonging to this relation */ index_oid_list = RelationGetIndexList(target_rel); /* For each index, recursively change its ownership */ foreach(i, index_oid_list) ATExecChangeOwner(lfirst_oid(i), newOwnerId, true, lockmode); list_free(index_oid_list); } if (tuple_class->relkind == RELKIND_RELATION || tuple_class->relkind == RELKIND_MATVIEW) { /* If it has a toast table, recurse to change its ownership */ if (tuple_class->reltoastrelid != InvalidOid) ATExecChangeOwner(tuple_class->reltoastrelid, newOwnerId, true, lockmode); /* If it has dependent sequences, recurse to change them too */ change_owner_recurse_to_sequences(relationOid, newOwnerId, lockmode); } } InvokeObjectPostAlterHook(RelationRelationId, relationOid, 0); ReleaseSysCache(tuple); heap_close(class_rel, RowExclusiveLock); relation_close(target_rel, NoLock); }
void check_of_type | ( | HeapTuple | typetuple | ) |
Definition at line 4301 of file tablecmds.c.
References AccessShareLock, Assert, ereport, errcode(), errmsg(), ERROR, format_type_be(), GETSTRUCT, HeapTupleGetOid, NoLock, OidIsValid, RelationData::rd_rel, relation_close(), relation_open(), and TYPTYPE_COMPOSITE.
Referenced by ATExecAddOf(), and transformOfType().
{ Form_pg_type typ = (Form_pg_type) GETSTRUCT(typetuple); bool typeOk = false; if (typ->typtype == TYPTYPE_COMPOSITE) { Relation typeRelation; Assert(OidIsValid(typ->typrelid)); typeRelation = relation_open(typ->typrelid, AccessShareLock); typeOk = (typeRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE); /* * Close the parent rel, but keep our AccessShareLock on it until xact * commit. That will prevent someone else from deleting or ALTERing * the type before the typed table creation/conversion commits. */ relation_close(typeRelation, NoLock); } if (!typeOk) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("type %s is not a composite type", format_type_be(HeapTupleGetOid(typetuple))))); }
void CheckTableNotInUse | ( | Relation | rel, | |
const char * | stmt | |||
) |
Definition at line 2589 of file tablecmds.c.
References AfterTriggerPendingOnRel(), ereport, errcode(), errmsg(), ERROR, RelationData::rd_isnailed, RelationData::rd_refcnt, RelationData::rd_rel, RelationGetRelationName, RelationGetRelid, and RELKIND_INDEX.
Referenced by AlterTable(), ATAddCheckConstraint(), ATExecAddColumn(), ATExecDropColumn(), ATExecDropConstraint(), ATSimpleRecursion(), ATTypedTableRecursion(), cluster_rel(), DefineVirtualRelation(), ExecRefreshMatView(), heap_drop_with_catalog(), index_drop(), reindex_index(), and truncate_check_rel().
{ int expected_refcnt; expected_refcnt = rel->rd_isnailed ? 2 : 1; if (rel->rd_refcnt != expected_refcnt) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), /* translator: first %s is a SQL command, eg ALTER TABLE */ errmsg("cannot %s \"%s\" because " "it is being used by active queries in this session", stmt, RelationGetRelationName(rel)))); if (rel->rd_rel->relkind != RELKIND_INDEX && AfterTriggerPendingOnRel(RelationGetRelid(rel))) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), /* translator: first %s is a SQL command, eg ALTER TABLE */ errmsg("cannot %s \"%s\" because " "it has pending trigger events", stmt, RelationGetRelationName(rel)))); }
Oid DefineRelation | ( | CreateStmt * | stmt, | |
char | relkind, | |||
Oid | ownerId | |||
) |
Definition at line 429 of file tablecmds.c.
References AccessExclusiveLock, ACL_CREATE, ACL_KIND_TABLESPACE, ACL_USAGE, aclcheck_error(), aclcheck_error_type(), ACLCHECK_OK, AddRelationNewConstraints(), allowSystemTableMods, Assert, CookedConstraint::attnum, RawColumnDefault::attnum, tupleDesc::attrs, BuildDescForRelation(), CommandCounterIncrement(), CreateStmt::constraints, CookedConstraint::contype, ColumnDef::cooked_default, ereport, errcode(), errmsg(), ERROR, CookedConstraint::expr, get_tablespace_name(), get_tablespace_oid(), GetDefaultTablespace(), GetUserId(), GLOBALTABLESPACE_OID, heap_create_with_catalog(), heap_reloptions(), CookedConstraint::inhcount, CreateStmt::inhRelations, InSecurityRestrictedOperation(), interpretOidsOption(), InvalidOid, CookedConstraint::is_local, CookedConstraint::is_no_inherit, lappend(), lfirst, list_concat(), MergeAttributes(), MyDatabaseTableSpace, CookedConstraint::name, NAMEDATALEN, NIL, NoLock, NULL, CreateStmt::ofTypename, OidIsValid, CreateStmt::oncommit, ONCOMMIT_NOOP, CreateStmt::options, palloc(), pg_tablespace_aclcheck(), pg_type_aclcheck(), RangeVarGetAndCheckCreationNamespace(), RawColumnDefault::raw_default, ColumnDef::raw_default, CreateStmt::relation, relation_close(), relation_open(), RELKIND_FOREIGN_TABLE, RELKIND_RELATION, RangeVar::relname, RangeVar::relpersistence, RELPERSISTENCE_TEMP, CookedConstraint::skip_validation, StoreCatalogInheritance(), StrNCpy, CreateStmt::tableElts, CreateStmt::tablespacename, tupleDesc::tdhasoid, transformRelOptions(), and typenameTypeId().
Referenced by DefineCompositeType(), DefineSequence(), DefineVirtualRelation(), intorel_startup(), and ProcessUtilitySlow().
{ char relname[NAMEDATALEN]; Oid namespaceId; List *schema = stmt->tableElts; Oid relationId; Oid tablespaceId; Relation rel; TupleDesc descriptor; List *inheritOids; List *old_constraints; bool localHasOids; int parentOidCount; List *rawDefaults; List *cookedDefaults; Datum reloptions; ListCell *listptr; AttrNumber attnum; static char *validnsps[] = HEAP_RELOPT_NAMESPACES; Oid ofTypeId; /* * Truncate relname to appropriate length (probably a waste of time, as * parser should have done this already). */ StrNCpy(relname, stmt->relation->relname, NAMEDATALEN); /* * Check consistency of arguments */ if (stmt->oncommit != ONCOMMIT_NOOP && stmt->relation->relpersistence != RELPERSISTENCE_TEMP) ereport(ERROR, (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("ON COMMIT can only be used on temporary tables"))); if (stmt->constraints != NIL && relkind == RELKIND_FOREIGN_TABLE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("constraints are not supported on foreign tables"))); /* * Look up the namespace in which we are supposed to create the relation, * check we have permission to create there, lock it against concurrent * drop, and mark stmt->relation as RELPERSISTENCE_TEMP if a temporary * namespace is selected. */ namespaceId = RangeVarGetAndCheckCreationNamespace(stmt->relation, NoLock, NULL); /* * Security check: disallow creating temp tables from security-restricted * code. This is needed because calling code might not expect untrusted * tables to appear in pg_temp at the front of its search path. */ if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP && InSecurityRestrictedOperation()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("cannot create temporary table within security-restricted operation"))); /* * Select tablespace to use. If not specified, use default tablespace * (which may in turn default to database's default). */ if (stmt->tablespacename) { tablespaceId = get_tablespace_oid(stmt->tablespacename, false); } else { tablespaceId = GetDefaultTablespace(stmt->relation->relpersistence); /* note InvalidOid is OK in this case */ } /* Check permissions except when using database's default */ if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace) { AclResult aclresult; aclresult = pg_tablespace_aclcheck(tablespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_TABLESPACE, get_tablespace_name(tablespaceId)); } /* In all cases disallow placing user relations in pg_global */ if (tablespaceId == GLOBALTABLESPACE_OID) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("only shared relations can be placed in pg_global tablespace"))); /* Identify user ID that will own the table */ if (!OidIsValid(ownerId)) ownerId = GetUserId(); /* * Parse and validate reloptions, if any. */ reloptions = transformRelOptions((Datum) 0, stmt->options, NULL, validnsps, true, false); (void) heap_reloptions(relkind, reloptions, true); if (stmt->ofTypename) { AclResult aclresult; ofTypeId = typenameTypeId(NULL, stmt->ofTypename); aclresult = pg_type_aclcheck(ofTypeId, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) aclcheck_error_type(aclresult, ofTypeId); } else ofTypeId = InvalidOid; /* * Look up inheritance ancestors and generate relation schema, including * inherited attributes. */ schema = MergeAttributes(schema, stmt->inhRelations, stmt->relation->relpersistence, &inheritOids, &old_constraints, &parentOidCount); /* * Create a tuple descriptor from the relation schema. Note that this * deals with column names, types, and NOT NULL constraints, but not * default values or CHECK constraints; we handle those below. */ descriptor = BuildDescForRelation(schema); localHasOids = interpretOidsOption(stmt->options, (relkind == RELKIND_RELATION || relkind == RELKIND_FOREIGN_TABLE)); descriptor->tdhasoid = (localHasOids || parentOidCount > 0); /* * Find columns with default values and prepare for insertion of the * defaults. Pre-cooked (that is, inherited) defaults go into a list of * CookedConstraint structs that we'll pass to heap_create_with_catalog, * while raw defaults go into a list of RawColumnDefault structs that will * be processed by AddRelationNewConstraints. (We can't deal with raw * expressions until we can do transformExpr.) * * We can set the atthasdef flags now in the tuple descriptor; this just * saves StoreAttrDefault from having to do an immediate update of the * pg_attribute rows. */ rawDefaults = NIL; cookedDefaults = NIL; attnum = 0; foreach(listptr, schema) { ColumnDef *colDef = lfirst(listptr); attnum++; if (colDef->raw_default != NULL) { RawColumnDefault *rawEnt; Assert(colDef->cooked_default == NULL); rawEnt = (RawColumnDefault *) palloc(sizeof(RawColumnDefault)); rawEnt->attnum = attnum; rawEnt->raw_default = colDef->raw_default; rawDefaults = lappend(rawDefaults, rawEnt); descriptor->attrs[attnum - 1]->atthasdef = true; } else if (colDef->cooked_default != NULL) { CookedConstraint *cooked; cooked = (CookedConstraint *) palloc(sizeof(CookedConstraint)); cooked->contype = CONSTR_DEFAULT; cooked->name = NULL; cooked->attnum = attnum; cooked->expr = colDef->cooked_default; cooked->skip_validation = false; cooked->is_local = true; /* not used for defaults */ cooked->inhcount = 0; /* ditto */ cooked->is_no_inherit = false; cookedDefaults = lappend(cookedDefaults, cooked); descriptor->attrs[attnum - 1]->atthasdef = true; } } /* * Create the relation. Inherited defaults and constraints are passed in * for immediate handling --- since they don't need parsing, they can be * stored immediately. */ relationId = heap_create_with_catalog(relname, namespaceId, tablespaceId, InvalidOid, InvalidOid, ofTypeId, ownerId, descriptor, list_concat(cookedDefaults, old_constraints), relkind, stmt->relation->relpersistence, false, false, localHasOids, parentOidCount, stmt->oncommit, reloptions, true, allowSystemTableMods, false); /* Store inheritance information for new rel. */ StoreCatalogInheritance(relationId, inheritOids); /* * We must bump the command counter to make the newly-created relation * tuple visible for opening. */ CommandCounterIncrement(); /* * Open the new relation and acquire exclusive lock on it. This isn't * really necessary for locking out other backends (since they can't see * the new rel anyway until we commit), but it keeps the lock manager from * complaining about deadlock risks. */ rel = relation_open(relationId, AccessExclusiveLock); /* * Now add any newly specified column default values and CHECK constraints * to the new relation. These are passed to us in the form of raw * parsetrees; we need to transform them to executable expression trees * before they can be added. The most convenient way to do that is to * apply the parser's transformExpr routine, but transformExpr doesn't * work unless we have a pre-existing relation. So, the transformation has * to be postponed to this final step of CREATE TABLE. */ if (rawDefaults || stmt->constraints) AddRelationNewConstraints(rel, rawDefaults, stmt->constraints, true, true, false); /* * Clean up. We keep lock on new relation (although it shouldn't be * visible to anyone else anyway, until commit). */ relation_close(rel, NoLock); return relationId; }
void ExecuteTruncate | ( | TruncateStmt * | stmt | ) |
Definition at line 945 of file tablecmds.c.
References AccessExclusiveLock, ACL_KIND_CLASS, aclcheck_error(), ACLCHECK_NOT_OWNER, AfterTriggerBeginQuery(), AfterTriggerEndQuery(), TruncateStmt::behavior, CheckTableForSerializableConflictIn(), CreateExecutorState(), DROP_CASCADE, DROP_RESTRICT, ereport, errmsg(), EState::es_num_result_relations, EState::es_result_relation_info, EState::es_result_relations, ExecASTruncateTriggers(), ExecBSTruncateTriggers(), find_all_inheritors(), FreeExecutorState(), GetCurrentSubTransactionId(), GetOldestMultiXactId(), getOwnedSequences(), GetUserId(), heap_close, heap_create_init_fork(), heap_open(), heap_openrv(), heap_truncate_check_FKs(), heap_truncate_find_FKs(), heap_truncate_one_rel(), RangeVar::inhOpt, InitResultRelInfo(), interpretInhOption(), lappend(), lappend_oid(), lfirst, lfirst_oid, list_length(), list_member_oid(), NIL, NoLock, NOTICE, NULL, OidIsValid, palloc(), pg_class_ownercheck(), RelationData::rd_createSubid, RelationData::rd_newRelfilenodeSubid, RelationData::rd_rel, RecentXmin, REINDEX_REL_PROCESS_TOAST, reindex_relation(), relation_close(), relation_open(), RelationGetRelationName, RelationGetRelid, TruncateStmt::relations, RelationSetNewRelfilenode(), RELPERSISTENCE_UNLOGGED, ResetSequence(), TruncateStmt::restart_seqs, and truncate_check_rel().
Referenced by standard_ProcessUtility().
{ List *rels = NIL; List *relids = NIL; List *seq_relids = NIL; EState *estate; ResultRelInfo *resultRelInfos; ResultRelInfo *resultRelInfo; SubTransactionId mySubid; ListCell *cell; /* * Open, exclusive-lock, and check all the explicitly-specified relations */ foreach(cell, stmt->relations) { RangeVar *rv = lfirst(cell); Relation rel; bool recurse = interpretInhOption(rv->inhOpt); Oid myrelid; rel = heap_openrv(rv, AccessExclusiveLock); myrelid = RelationGetRelid(rel); /* don't throw error for "TRUNCATE foo, foo" */ if (list_member_oid(relids, myrelid)) { heap_close(rel, AccessExclusiveLock); continue; } truncate_check_rel(rel); rels = lappend(rels, rel); relids = lappend_oid(relids, myrelid); if (recurse) { ListCell *child; List *children; children = find_all_inheritors(myrelid, AccessExclusiveLock, NULL); foreach(child, children) { Oid childrelid = lfirst_oid(child); if (list_member_oid(relids, childrelid)) continue; /* find_all_inheritors already got lock */ rel = heap_open(childrelid, NoLock); truncate_check_rel(rel); rels = lappend(rels, rel); relids = lappend_oid(relids, childrelid); } } } /* * In CASCADE mode, suck in all referencing relations as well. This * requires multiple iterations to find indirectly-dependent relations. At * each phase, we need to exclusive-lock new rels before looking for their * dependencies, else we might miss something. Also, we check each rel as * soon as we open it, to avoid a faux pas such as holding lock for a long * time on a rel we have no permissions for. */ if (stmt->behavior == DROP_CASCADE) { for (;;) { List *newrelids; newrelids = heap_truncate_find_FKs(relids); if (newrelids == NIL) break; /* nothing else to add */ foreach(cell, newrelids) { Oid relid = lfirst_oid(cell); Relation rel; rel = heap_open(relid, AccessExclusiveLock); ereport(NOTICE, (errmsg("truncate cascades to table \"%s\"", RelationGetRelationName(rel)))); truncate_check_rel(rel); rels = lappend(rels, rel); relids = lappend_oid(relids, relid); } } } /* * Check foreign key references. In CASCADE mode, this should be * unnecessary since we just pulled in all the references; but as a * cross-check, do it anyway if in an Assert-enabled build. */ #ifdef USE_ASSERT_CHECKING heap_truncate_check_FKs(rels, false); #else if (stmt->behavior == DROP_RESTRICT) heap_truncate_check_FKs(rels, false); #endif /* * If we are asked to restart sequences, find all the sequences, lock them * (we need AccessExclusiveLock for ResetSequence), and check permissions. * We want to do this early since it's pointless to do all the truncation * work only to fail on sequence permissions. */ if (stmt->restart_seqs) { foreach(cell, rels) { Relation rel = (Relation) lfirst(cell); List *seqlist = getOwnedSequences(RelationGetRelid(rel)); ListCell *seqcell; foreach(seqcell, seqlist) { Oid seq_relid = lfirst_oid(seqcell); Relation seq_rel; seq_rel = relation_open(seq_relid, AccessExclusiveLock); /* This check must match AlterSequence! */ if (!pg_class_ownercheck(seq_relid, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, RelationGetRelationName(seq_rel)); seq_relids = lappend_oid(seq_relids, seq_relid); relation_close(seq_rel, NoLock); } } } /* Prepare to catch AFTER triggers. */ AfterTriggerBeginQuery(); /* * To fire triggers, we'll need an EState as well as a ResultRelInfo for * each relation. We don't need to call ExecOpenIndices, though. */ estate = CreateExecutorState(); resultRelInfos = (ResultRelInfo *) palloc(list_length(rels) * sizeof(ResultRelInfo)); resultRelInfo = resultRelInfos; foreach(cell, rels) { Relation rel = (Relation) lfirst(cell); InitResultRelInfo(resultRelInfo, rel, 0, /* dummy rangetable index */ 0); resultRelInfo++; } estate->es_result_relations = resultRelInfos; estate->es_num_result_relations = list_length(rels); /* * Process all BEFORE STATEMENT TRUNCATE triggers before we begin * truncating (this is because one of them might throw an error). Also, if * we were to allow them to prevent statement execution, that would need * to be handled here. */ resultRelInfo = resultRelInfos; foreach(cell, rels) { estate->es_result_relation_info = resultRelInfo; ExecBSTruncateTriggers(estate, resultRelInfo); resultRelInfo++; } /* * OK, truncate each table. */ mySubid = GetCurrentSubTransactionId(); foreach(cell, rels) { Relation rel = (Relation) lfirst(cell); /* * Normally, we need a transaction-safe truncation here. However, if * the table was either created in the current (sub)transaction or has * a new relfilenode in the current (sub)transaction, then we can just * truncate it in-place, because a rollback would cause the whole * table or the current physical file to be thrown away anyway. */ if (rel->rd_createSubid == mySubid || rel->rd_newRelfilenodeSubid == mySubid) { /* Immediate, non-rollbackable truncation is OK */ heap_truncate_one_rel(rel); } else { Oid heap_relid; Oid toast_relid; MultiXactId minmulti; /* * This effectively deletes all rows in the table, and may be done * in a serializable transaction. In that case we must record a * rw-conflict in to this transaction from each transaction * holding a predicate lock on the table. */ CheckTableForSerializableConflictIn(rel); minmulti = GetOldestMultiXactId(); /* * Need the full transaction-safe pushups. * * Create a new empty storage file for the relation, and assign it * as the relfilenode value. The old storage file is scheduled for * deletion at commit. */ RelationSetNewRelfilenode(rel, RecentXmin, minmulti); if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED) heap_create_init_fork(rel); heap_relid = RelationGetRelid(rel); toast_relid = rel->rd_rel->reltoastrelid; /* * The same for the toast table, if any. */ if (OidIsValid(toast_relid)) { rel = relation_open(toast_relid, AccessExclusiveLock); RelationSetNewRelfilenode(rel, RecentXmin, minmulti); if (rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED) heap_create_init_fork(rel); heap_close(rel, NoLock); } /* * Reconstruct the indexes to match, and we're done. */ reindex_relation(heap_relid, REINDEX_REL_PROCESS_TOAST); } } /* * Restart owned sequences if we were asked to. */ foreach(cell, seq_relids) { Oid seq_relid = lfirst_oid(cell); ResetSequence(seq_relid); } /* * Process all AFTER STATEMENT TRUNCATE triggers. */ resultRelInfo = resultRelInfos; foreach(cell, rels) { estate->es_result_relation_info = resultRelInfo; ExecASTruncateTriggers(estate, resultRelInfo); resultRelInfo++; } /* Handle queued AFTER triggers */ AfterTriggerEndQuery(estate); /* We can clean up the EState now */ FreeExecutorState(estate); /* And close the rels (can't do this while EState still holds refs) */ foreach(cell, rels) { Relation rel = (Relation) lfirst(cell); heap_close(rel, NoLock); } }
void find_composite_type_dependencies | ( | Oid | typeOid, | |
Relation | origRelation, | |||
const char * | origTypeName | |||
) |
Definition at line 4147 of file tablecmds.c.
References AccessShareLock, Anum_pg_depend_refclassid, Anum_pg_depend_refobjid, tupleDesc::attrs, BTEqualStrategyNumber, DependReferenceIndexId, DependRelationId, ereport, errcode(), errmsg(), ERROR, find_composite_type_dependencies(), get_array_type(), GETSTRUCT, heap_open(), HeapTupleIsValid, NameStr, ObjectIdGetDatum, OidIsValid, RelationData::rd_att, RelationData::rd_rel, relation_close(), relation_open(), RelationGetRelationName, RelationRelationId, RELKIND_COMPOSITE_TYPE, RELKIND_FOREIGN_TABLE, RELKIND_MATVIEW, RELKIND_RELATION, ScanKeyInit(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), and TypeRelationId.
Referenced by ATPrepAlterColumnType(), ATRewriteTables(), find_composite_type_dependencies(), and get_rels_with_domain().
{ Relation depRel; ScanKeyData key[2]; SysScanDesc depScan; HeapTuple depTup; Oid arrayOid; /* * We scan pg_depend to find those things that depend on the rowtype. (We * assume we can ignore refobjsubid for a rowtype.) */ depRel = heap_open(DependRelationId, AccessShareLock); ScanKeyInit(&key[0], Anum_pg_depend_refclassid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(TypeRelationId)); ScanKeyInit(&key[1], Anum_pg_depend_refobjid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(typeOid)); depScan = systable_beginscan(depRel, DependReferenceIndexId, true, SnapshotNow, 2, key); while (HeapTupleIsValid(depTup = systable_getnext(depScan))) { Form_pg_depend pg_depend = (Form_pg_depend) GETSTRUCT(depTup); Relation rel; Form_pg_attribute att; /* Ignore dependees that aren't user columns of relations */ /* (we assume system columns are never of rowtypes) */ if (pg_depend->classid != RelationRelationId || pg_depend->objsubid <= 0) continue; rel = relation_open(pg_depend->objid, AccessShareLock); att = rel->rd_att->attrs[pg_depend->objsubid - 1]; if (rel->rd_rel->relkind == RELKIND_RELATION || rel->rd_rel->relkind == RELKIND_MATVIEW) { if (origTypeName) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it", origTypeName, RelationGetRelationName(rel), NameStr(att->attname)))); else if (origRelation->rd_rel->relkind == RELKIND_COMPOSITE_TYPE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot alter type \"%s\" because column \"%s.%s\" uses it", RelationGetRelationName(origRelation), RelationGetRelationName(rel), NameStr(att->attname)))); else if (origRelation->rd_rel->relkind == RELKIND_FOREIGN_TABLE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot alter foreign table \"%s\" because column \"%s.%s\" uses its row type", RelationGetRelationName(origRelation), RelationGetRelationName(rel), NameStr(att->attname)))); else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot alter table \"%s\" because column \"%s.%s\" uses its row type", RelationGetRelationName(origRelation), RelationGetRelationName(rel), NameStr(att->attname)))); } else if (OidIsValid(rel->rd_rel->reltype)) { /* * A view or composite type itself isn't a problem, but we must * recursively check for indirect dependencies via its rowtype. */ find_composite_type_dependencies(rel->rd_rel->reltype, origRelation, origTypeName); } relation_close(rel, AccessShareLock); } systable_endscan(depScan); relation_close(depRel, AccessShareLock); /* * If there's an array type for the rowtype, must check for uses of it, * too. */ arrayOid = get_array_type(typeOid); if (OidIsValid(arrayOid)) find_composite_type_dependencies(arrayOid, origRelation, origTypeName); }
void PreCommit_on_commit_actions | ( | void | ) |
Definition at line 10230 of file tablecmds.c.
References Assert, CommandCounterIncrement(), OnCommitItem::deleting_subid, DROP_CASCADE, heap_truncate(), InvalidSubTransactionId, lappend_oid(), lfirst, MyXactAccessedTempRel, NIL, OnCommitItem::oncommit, ONCOMMIT_DELETE_ROWS, ONCOMMIT_DROP, ONCOMMIT_NOOP, ONCOMMIT_PRESERVE_ROWS, PERFORM_DELETION_INTERNAL, performDeletion(), and OnCommitItem::relid.
Referenced by CommitTransaction(), and PrepareTransaction().
{ ListCell *l; List *oids_to_truncate = NIL; foreach(l, on_commits) { OnCommitItem *oc = (OnCommitItem *) lfirst(l); /* Ignore entry if already dropped in this xact */ if (oc->deleting_subid != InvalidSubTransactionId) continue; switch (oc->oncommit) { case ONCOMMIT_NOOP: case ONCOMMIT_PRESERVE_ROWS: /* Do nothing (there shouldn't be such entries, actually) */ break; case ONCOMMIT_DELETE_ROWS: /* * If this transaction hasn't accessed any temporary * relations, we can skip truncating ON COMMIT DELETE ROWS * tables, as they must still be empty. */ if (MyXactAccessedTempRel) oids_to_truncate = lappend_oid(oids_to_truncate, oc->relid); break; case ONCOMMIT_DROP: { ObjectAddress object; object.classId = RelationRelationId; object.objectId = oc->relid; object.objectSubId = 0; /* * Since this is an automatic drop, rather than one * directly initiated by the user, we pass the * PERFORM_DELETION_INTERNAL flag. */ performDeletion(&object, DROP_CASCADE, PERFORM_DELETION_INTERNAL); /* * Note that table deletion will call * remove_on_commit_action, so the entry should get marked * as deleted. */ Assert(oc->deleting_subid != InvalidSubTransactionId); break; } } } if (oids_to_truncate != NIL) { heap_truncate(oids_to_truncate); CommandCounterIncrement(); /* XXX needed? */ } }
Definition at line 10387 of file tablecmds.c.
References ACL_KIND_CLASS, aclcheck_error(), ACLCHECK_NOT_OWNER, ereport, errcode(), errmsg(), ERROR, get_rel_relkind(), GetUserId(), OidIsValid, pg_class_ownercheck(), DropRelationCallbackState::relkind, RELKIND_MATVIEW, RELKIND_RELATION, RELKIND_TOASTVALUE, and RangeVar::relname.
Referenced by cluster(), ExecRefreshMatView(), and ReindexTable().
{ char relkind; /* Nothing to do if the relation was not found. */ if (!OidIsValid(relId)) return; /* * If the relation does exist, check whether it's an index. But note that * the relation might have been dropped between the time we did the name * lookup and now. In that case, there's nothing to do. */ relkind = get_rel_relkind(relId); if (!relkind) return; if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE && relkind != RELKIND_MATVIEW) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table or materialized view", relation->relname))); /* Check permissions */ if (!pg_class_ownercheck(relId, GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, relation->relname); }
void register_on_commit_action | ( | Oid | relid, | |
OnCommitAction | action | |||
) |
Definition at line 10176 of file tablecmds.c.
References CacheMemoryContext, OnCommitItem::creating_subid, OnCommitItem::deleting_subid, GetCurrentSubTransactionId(), lcons(), MemoryContextSwitchTo(), OnCommitItem::oncommit, ONCOMMIT_NOOP, ONCOMMIT_PRESERVE_ROWS, palloc(), and OnCommitItem::relid.
Referenced by heap_create_with_catalog().
{ OnCommitItem *oc; MemoryContext oldcxt; /* * We needn't bother registering the relation unless there is an ON COMMIT * action we need to take. */ if (action == ONCOMMIT_NOOP || action == ONCOMMIT_PRESERVE_ROWS) return; oldcxt = MemoryContextSwitchTo(CacheMemoryContext); oc = (OnCommitItem *) palloc(sizeof(OnCommitItem)); oc->relid = relid; oc->oncommit = action; oc->creating_subid = GetCurrentSubTransactionId(); oc->deleting_subid = InvalidSubTransactionId; on_commits = lcons(oc, on_commits); MemoryContextSwitchTo(oldcxt); }
void remove_on_commit_action | ( | Oid | relid | ) |
Definition at line 10207 of file tablecmds.c.
References OnCommitItem::deleting_subid, GetCurrentSubTransactionId(), lfirst, and OnCommitItem::relid.
Referenced by heap_drop_with_catalog().
{ ListCell *l; foreach(l, on_commits) { OnCommitItem *oc = (OnCommitItem *) lfirst(l); if (oc->relid == relid) { oc->deleting_subid = GetCurrentSubTransactionId(); break; } } }
void RemoveRelations | ( | DropStmt * | drop | ) |
Definition at line 746 of file tablecmds.c.
References AcceptInvalidationMessages(), add_exact_object_address(), Assert, DropStmt::behavior, ObjectAddress::classId, DropRelationCallbackState::concurrent, DropStmt::concurrent, DROP_CASCADE, DropErrorMsgNonExistent(), elog, ereport, errcode(), errmsg(), ERROR, free_object_addresses(), DropRelationCallbackState::heapOid, lfirst, list_length(), makeRangeVarFromNameList(), DropStmt::missing_ok, new_object_addresses(), OBJECT_FOREIGN_TABLE, OBJECT_INDEX, OBJECT_MATVIEW, OBJECT_SEQUENCE, OBJECT_TABLE, OBJECT_VIEW, ObjectAddress::objectId, DropStmt::objects, ObjectAddress::objectSubId, OidIsValid, performMultipleDeletions(), RangeVarCallbackForDropRelation(), RangeVarGetRelidExtended(), DropRelationCallbackState::relkind, RangeVar::relname, and DropStmt::removeType.
Referenced by ExecDropStmt().
{ ObjectAddresses *objects; char relkind; ListCell *cell; int flags = 0; LOCKMODE lockmode = AccessExclusiveLock; /* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */ if (drop->concurrent) { flags |= PERFORM_DELETION_CONCURRENTLY; lockmode = ShareUpdateExclusiveLock; Assert(drop->removeType == OBJECT_INDEX); if (list_length(drop->objects) != 1) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("DROP INDEX CONCURRENTLY does not support dropping multiple objects"))); if (drop->behavior == DROP_CASCADE) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("DROP INDEX CONCURRENTLY does not support CASCADE"))); } /* * First we identify all the relations, then we delete them in a single * performMultipleDeletions() call. This is to avoid unwanted DROP * RESTRICT errors if one of the relations depends on another. */ /* Determine required relkind */ switch (drop->removeType) { case OBJECT_TABLE: relkind = RELKIND_RELATION; break; case OBJECT_INDEX: relkind = RELKIND_INDEX; break; case OBJECT_SEQUENCE: relkind = RELKIND_SEQUENCE; break; case OBJECT_VIEW: relkind = RELKIND_VIEW; break; case OBJECT_MATVIEW: relkind = RELKIND_MATVIEW; break; case OBJECT_FOREIGN_TABLE: relkind = RELKIND_FOREIGN_TABLE; break; default: elog(ERROR, "unrecognized drop object type: %d", (int) drop->removeType); relkind = 0; /* keep compiler quiet */ break; } /* Lock and validate each relation; build a list of object addresses */ objects = new_object_addresses(); foreach(cell, drop->objects) { RangeVar *rel = makeRangeVarFromNameList((List *) lfirst(cell)); Oid relOid; ObjectAddress obj; struct DropRelationCallbackState state; /* * These next few steps are a great deal like relation_openrv, but we * don't bother building a relcache entry since we don't need it. * * Check for shared-cache-inval messages before trying to access the * relation. This is needed to cover the case where the name * identifies a rel that has been dropped and recreated since the * start of our transaction: if we don't flush the old syscache entry, * then we'll latch onto that entry and suffer an error later. */ AcceptInvalidationMessages(); /* Look up the appropriate relation using namespace search. */ state.relkind = relkind; state.heapOid = InvalidOid; state.concurrent = drop->concurrent; relOid = RangeVarGetRelidExtended(rel, lockmode, true, false, RangeVarCallbackForDropRelation, (void *) &state); /* Not there? */ if (!OidIsValid(relOid)) { DropErrorMsgNonExistent(rel->relname, relkind, drop->missing_ok); continue; } /* OK, we're ready to delete this one */ obj.classId = RelationRelationId; obj.objectId = relOid; obj.objectSubId = 0; add_exact_object_address(&obj, objects); } performMultipleDeletions(objects, drop->behavior, flags); free_object_addresses(objects); }
Oid renameatt | ( | RenameStmt * | stmt | ) |
Definition at line 2278 of file tablecmds.c.
References AccessExclusiveLock, RenameStmt::behavior, ereport, errmsg(), RangeVar::inhOpt, interpretInhOption(), RenameStmt::missing_ok, RenameStmt::newname, NOTICE, NULL, OidIsValid, RangeVarCallbackForRenameAttribute(), RangeVarGetRelidExtended(), RenameStmt::relation, RangeVar::relname, renameatt_internal(), and RenameStmt::subname.
Referenced by ExecRenameStmt().
{ Oid relid; /* lock level taken here should match renameatt_internal */ relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, stmt->missing_ok, false, RangeVarCallbackForRenameAttribute, NULL); if (!OidIsValid(relid)) { ereport(NOTICE, (errmsg("relation \"%s\" does not exist, skipping", stmt->relation->relname))); return InvalidOid; } renameatt_internal(relid, stmt->subname, /* old att name */ stmt->newname, /* new att name */ interpretInhOption(stmt->relation->inhOpt), /* recursive? */ false, /* recursing? */ 0, /* expected inhcount */ stmt->behavior); /* This is an ALTER TABLE command so it's about the relid */ return relid; }
Oid RenameConstraint | ( | RenameStmt * | stmt | ) |
Definition at line 2409 of file tablecmds.c.
References AccessExclusiveLock, checkDomainOwner(), elog, ERROR, heap_close, heap_open(), HeapTupleIsValid, RangeVar::inhOpt, interpretInhOption(), makeTypeNameFromNameList(), RenameStmt::newname, NoLock, NULL, RenameStmt::object, OBJECT_DOMAIN, ObjectIdGetDatum, RangeVarCallbackForRenameAttribute(), RangeVarGetRelidExtended(), RenameStmt::relation, RenameStmt::relationType, ReleaseSysCache(), rename_constraint_internal(), RowExclusiveLock, SearchSysCache1, RenameStmt::subname, typenameTypeId(), TYPEOID, and TypeRelationId.
Referenced by ExecRenameStmt().
{ Oid relid = InvalidOid; Oid typid = InvalidOid; if (stmt->relationType == OBJECT_DOMAIN) { Relation rel; HeapTuple tup; typid = typenameTypeId(NULL, makeTypeNameFromNameList(stmt->object)); rel = heap_open(TypeRelationId, RowExclusiveLock); tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid)); if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for type %u", typid); checkDomainOwner(tup); ReleaseSysCache(tup); heap_close(rel, NoLock); } else { /* lock level taken here should match rename_constraint_internal */ relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, false, false, RangeVarCallbackForRenameAttribute, NULL); } return rename_constraint_internal(relid, typid, stmt->subname, stmt->newname, stmt->relation ? interpretInhOption(stmt->relation->inhOpt) : false, /* recursive? */ false, /* recursing? */ 0 /* expected inhcount */ ); }
Oid RenameRelation | ( | RenameStmt * | stmt | ) |
Definition at line 2451 of file tablecmds.c.
References AccessExclusiveLock, ereport, errmsg(), RenameStmt::missing_ok, NOTICE, OidIsValid, RangeVarCallbackForAlterRelation(), RangeVarGetRelidExtended(), RenameStmt::relation, and RenameRelationInternal().
Referenced by ExecRenameStmt().
{ Oid relid; /* * Grab an exclusive lock on the target table, index, sequence or view, * which we will NOT release until end of transaction. * * Lock level used here should match RenameRelationInternal, to avoid lock * escalation. */ relid = RangeVarGetRelidExtended(stmt->relation, AccessExclusiveLock, stmt->missing_ok, false, RangeVarCallbackForAlterRelation, (void *) stmt); if (!OidIsValid(relid)) { ereport(NOTICE, (errmsg("relation \"%s\" does not exist, skipping", stmt->relation->relname))); return InvalidOid; } /* Do the work */ RenameRelationInternal(relid, stmt->newname, false); return relid; }
Definition at line 2491 of file tablecmds.c.
References AccessExclusiveLock, CatalogUpdateIndexes(), elog, ereport, errcode(), errmsg(), ERROR, get_index_constraint(), get_relname_relid(), GETSTRUCT, heap_close, heap_freetuple(), heap_open(), HeapTupleIsValid, InvalidOid, InvokeObjectPostAlterHookArg, namestrcpy(), NoLock, ObjectIdGetDatum, OidIsValid, RelationData::rd_rel, relation_close(), relation_open(), RelationGetNamespace, RelationRelationId, RELKIND_INDEX, RELOID, RenameConstraintById(), RenameTypeInternal(), RowExclusiveLock, SearchSysCacheCopy1, simple_heap_update(), and HeapTupleData::t_self.
Referenced by ATExecAddIndexConstraint(), finish_heap_swap(), rename_constraint_internal(), RenameRelation(), and RenameType().
{ Relation targetrelation; Relation relrelation; /* for RELATION relation */ HeapTuple reltup; Form_pg_class relform; Oid namespaceId; /* * Grab an exclusive lock on the target table, index, sequence or view, * which we will NOT release until end of transaction. */ targetrelation = relation_open(myrelid, AccessExclusiveLock); namespaceId = RelationGetNamespace(targetrelation); /* * Find relation's pg_class tuple, and make sure newrelname isn't in use. */ relrelation = heap_open(RelationRelationId, RowExclusiveLock); reltup = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(myrelid)); if (!HeapTupleIsValid(reltup)) /* shouldn't happen */ elog(ERROR, "cache lookup failed for relation %u", myrelid); relform = (Form_pg_class) GETSTRUCT(reltup); if (get_relname_relid(newrelname, namespaceId) != InvalidOid) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_TABLE), errmsg("relation \"%s\" already exists", newrelname))); /* * Update pg_class tuple with new relname. (Scribbling on reltup is OK * because it's a copy...) */ namestrcpy(&(relform->relname), newrelname); simple_heap_update(relrelation, &reltup->t_self, reltup); /* keep the system catalog indexes current */ CatalogUpdateIndexes(relrelation, reltup); InvokeObjectPostAlterHookArg(RelationRelationId, myrelid, 0, InvalidOid, is_internal); heap_freetuple(reltup); heap_close(relrelation, RowExclusiveLock); /* * Also rename the associated type, if any. */ if (OidIsValid(targetrelation->rd_rel->reltype)) RenameTypeInternal(targetrelation->rd_rel->reltype, newrelname, namespaceId); /* * Also rename the associated constraint, if any. */ if (targetrelation->rd_rel->relkind == RELKIND_INDEX) { Oid constraintId = get_index_constraint(myrelid); if (OidIsValid(constraintId)) RenameConstraintById(constraintId, newrelname); } /* * Close rel, but keep exclusive lock! */ relation_close(targetrelation, NoLock); }
Definition at line 2033 of file tablecmds.c.
References CacheInvalidateRelcacheByTuple(), CatalogUpdateIndexes(), elog, ERROR, GETSTRUCT, heap_close, heap_freetuple(), heap_open(), HeapTupleIsValid, ObjectIdGetDatum, RelationRelationId, RELOID, RowExclusiveLock, SearchSysCacheCopy1, simple_heap_update(), and HeapTupleData::t_self.
Referenced by acquire_inherited_sample_rows(), and StoreCatalogInheritance1().
{ Relation relationRelation; HeapTuple tuple; Form_pg_class classtuple; /* * Fetch a modifiable copy of the tuple, modify it, update pg_class. */ relationRelation = heap_open(RelationRelationId, RowExclusiveLock); tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(relationId)); if (!HeapTupleIsValid(tuple)) elog(ERROR, "cache lookup failed for relation %u", relationId); classtuple = (Form_pg_class) GETSTRUCT(tuple); if (classtuple->relhassubclass != relhassubclass) { classtuple->relhassubclass = relhassubclass; simple_heap_update(relationRelation, &tuple->t_self, tuple); /* keep the catalog indexes up to date */ CatalogUpdateIndexes(relationRelation, tuple); } else { /* no need to change tuple, but force relcache rebuild anyway */ CacheInvalidateRelcacheByTuple(tuple); } heap_freetuple(tuple); heap_close(relationRelation, RowExclusiveLock); }