diff options
Diffstat (limited to 'src/backend/commands')
-rw-r--r-- | src/backend/commands/alter.c | 4 | ||||
-rw-r--r-- | src/backend/commands/indexcmds.c | 25 | ||||
-rw-r--r-- | src/backend/commands/tablecmds.c | 134 | ||||
-rw-r--r-- | src/backend/commands/trigger.c | 25 |
4 files changed, 121 insertions, 67 deletions
diff --git a/src/backend/commands/alter.c b/src/backend/commands/alter.c index 0f4e0f701ab..91df1a0aca7 100644 --- a/src/backend/commands/alter.c +++ b/src/backend/commands/alter.c @@ -105,9 +105,8 @@ ExecRenameStmt(RenameStmt *stmt) { Oid relid; - CheckRelationOwnership(stmt->relation, true); - relid = RangeVarGetRelid(stmt->relation, false); + CheckRelationOwnership(relid, true); switch (stmt->renameType) { @@ -223,7 +222,6 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt) case OBJECT_TABLE: case OBJECT_VIEW: case OBJECT_FOREIGN_TABLE: - CheckRelationOwnership(stmt->relation, true); AlterTableNamespace(stmt->relation, stmt->newschema, stmt->objectType, AccessExclusiveLock); break; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index ab73567ad2a..1a0ee84963d 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -76,7 +76,8 @@ static char *ChooseIndexNameAddition(List *colnames); * DefineIndex * Creates a new index. * - * 'heapRelation': the relation the index will apply to. + * 'relationId': the OID of the heap relation on which the index is to be + * created * 'indexRelationName': the name for the new index, or NULL to indicate * that a nonconflicting default name should be picked. * 'indexRelationId': normally InvalidOid, but during bootstrap can be @@ -105,7 +106,7 @@ static char *ChooseIndexNameAddition(List *colnames); * 'concurrent': avoid blocking writers to the table while building. */ void -DefineIndex(RangeVar *heapRelation, +DefineIndex(Oid relationId, char *indexRelationName, Oid indexRelationId, char *accessMethodName, @@ -128,7 +129,6 @@ DefineIndex(RangeVar *heapRelation, Oid *collationObjectId; Oid *classObjectId; Oid accessMethodId; - Oid relationId; Oid namespaceId; Oid tablespaceId; List *indexColNames; @@ -148,6 +148,7 @@ DefineIndex(RangeVar *heapRelation, int n_old_snapshots; LockRelId heaprelid; LOCKTAG heaplocktag; + LOCKMODE lockmode; Snapshot snapshot; int i; @@ -166,14 +167,18 @@ DefineIndex(RangeVar *heapRelation, INDEX_MAX_KEYS))); /* - * Open heap relation, acquire a suitable lock on it, remember its OID - * * Only SELECT ... FOR UPDATE/SHARE are allowed while doing a standard * index build; but for concurrent builds we allow INSERT/UPDATE/DELETE * (but not VACUUM). + * + * NB: Caller is responsible for making sure that relationId refers + * to the relation on which the index should be built; except in bootstrap + * mode, this will typically require the caller to have already locked + * the relation. To avoid lock upgrade hazards, that lock should be at + * least as strong as the one we take here. */ - rel = heap_openrv(heapRelation, - (concurrent ? ShareUpdateExclusiveLock : ShareLock)); + lockmode = concurrent ? ShareUpdateExclusiveLock : ShareLock; + rel = heap_open(relationId, lockmode); relationId = RelationGetRelid(rel); namespaceId = RelationGetNamespace(rel); @@ -191,12 +196,12 @@ DefineIndex(RangeVar *heapRelation, ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot create index on foreign table \"%s\"", - heapRelation->relname))); + RelationGetRelationName(rel)))); else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a table", - heapRelation->relname))); + RelationGetRelationName(rel)))); } /* @@ -503,7 +508,7 @@ DefineIndex(RangeVar *heapRelation, */ /* Open and lock the parent heap relation */ - rel = heap_openrv(heapRelation, ShareUpdateExclusiveLock); + rel = heap_open(relationId, ShareUpdateExclusiveLock); /* And the target index relation */ indexRelation = index_open(indexRelationId, RowExclusiveLock); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index adf278bb1fa..ae62c698499 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -73,6 +73,7 @@ #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" @@ -272,7 +273,8 @@ static void checkFkeyPermissions(Relation rel, int16 *attnums, int natts); static void validateForeignKeyConstraint(char *conname, Relation rel, Relation pkrel, Oid pkindOid, Oid constraintOid); -static void createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, +static void createForeignKeyTriggers(Relation rel, Oid refRelOid, + Constraint *fkconstraint, Oid constraintOid, Oid indexOid); static void ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode); static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, @@ -346,7 +348,8 @@ static bool ATColumnChangeRequiresRewrite(Node *expr, AttrNumber varattno); static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, AlterTableCmd *cmd, LOCKMODE lockmode); static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode); -static void ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode); +static void ATPostAlterTypeParse(Oid oldRelId, Oid refRelId, char *cmd, + List **wqueue, LOCKMODE lockmode); static void change_owner_fix_column_acls(Oid relationOid, Oid oldOwnerId, Oid newOwnerId); static void change_owner_recurse_to_sequences(Oid relationOid, @@ -2378,15 +2381,13 @@ CheckTableNotInUse(Relation rel, const char *stmt) * rather than reassess it at lower levels. */ void -AlterTable(AlterTableStmt *stmt) +AlterTable(Oid relid, AlterTableStmt *stmt) { Relation rel; LOCKMODE lockmode = AlterTableGetLockLevel(stmt->cmds); - /* - * Acquire same level of lock as already acquired during parsing. - */ - rel = relation_openrv(stmt->relation, lockmode); + /* Caller is required to provide an adequate lock. */ + rel = relation_open(relid, NoLock); CheckTableNotInUse(rel, "ALTER TABLE"); @@ -5153,7 +5154,7 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel, /* The IndexStmt has already been through transformIndexStmt */ - DefineIndex(stmt->relation, /* relation */ + DefineIndex(RelationGetRelid(rel), /* relation */ stmt->idxname, /* index name */ InvalidOid, /* no predefined OID */ stmt->accessMethod, /* am name */ @@ -5465,7 +5466,10 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, * table; trying to start with a lesser lock will just create a risk of * deadlock.) */ - pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock); + if (OidIsValid(fkconstraint->old_pktable_oid)) + pkrel = heap_open(fkconstraint->old_pktable_oid, AccessExclusiveLock); + else + pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock); /* * Validity checks (permission checks wait till we have the column @@ -5713,7 +5717,8 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, /* * Create the triggers that will enforce the constraint. */ - createForeignKeyTriggers(rel, fkconstraint, constrOid, indexOid); + createForeignKeyTriggers(rel, RelationGetRelid(pkrel), fkconstraint, + constrOid, indexOid); /* * Tell Phase 3 to check that the constraint is satisfied by existing rows. @@ -6202,14 +6207,14 @@ validateForeignKeyConstraint(char *conname, } static void -CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint, +CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid, bool on_insert) { CreateTrigStmt *fk_trigger; fk_trigger = makeNode(CreateTrigStmt); fk_trigger->trigname = "RI_ConstraintTrigger"; - fk_trigger->relation = myRel; + fk_trigger->relation = NULL; fk_trigger->row = true; fk_trigger->timing = TRIGGER_TYPE_AFTER; @@ -6230,10 +6235,11 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint, fk_trigger->isconstraint = true; fk_trigger->deferrable = fkconstraint->deferrable; fk_trigger->initdeferred = fkconstraint->initdeferred; - fk_trigger->constrrel = fkconstraint->pktable; + fk_trigger->constrrel = NULL; fk_trigger->args = NIL; - (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true); + (void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid, + indexOid, true); /* Make changes-so-far visible */ CommandCounterIncrement(); @@ -6243,18 +6249,13 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint, * Create the triggers that implement an FK constraint. */ static void -createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, +createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint, Oid constraintOid, Oid indexOid) { - RangeVar *myRel; + Oid myRelOid; CreateTrigStmt *fk_trigger; - /* - * Reconstruct a RangeVar for my relation (not passed in, unfortunately). - */ - myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)), - pstrdup(RelationGetRelationName(rel)), - -1); + myRelOid = RelationGetRelid(rel); /* Make changes-so-far visible */ CommandCounterIncrement(); @@ -6265,14 +6266,14 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, */ fk_trigger = makeNode(CreateTrigStmt); fk_trigger->trigname = "RI_ConstraintTrigger"; - fk_trigger->relation = fkconstraint->pktable; + fk_trigger->relation = NULL; fk_trigger->row = true; fk_trigger->timing = TRIGGER_TYPE_AFTER; fk_trigger->events = TRIGGER_TYPE_DELETE; fk_trigger->columns = NIL; fk_trigger->whenClause = NULL; fk_trigger->isconstraint = true; - fk_trigger->constrrel = myRel; + fk_trigger->constrrel = NULL; switch (fkconstraint->fk_del_action) { case FKCONSTR_ACTION_NOACTION: @@ -6307,7 +6308,8 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, } fk_trigger->args = NIL; - (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true); + (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid, + indexOid, true); /* Make changes-so-far visible */ CommandCounterIncrement(); @@ -6318,14 +6320,14 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, */ fk_trigger = makeNode(CreateTrigStmt); fk_trigger->trigname = "RI_ConstraintTrigger"; - fk_trigger->relation = fkconstraint->pktable; + fk_trigger->relation = NULL; fk_trigger->row = true; fk_trigger->timing = TRIGGER_TYPE_AFTER; fk_trigger->events = TRIGGER_TYPE_UPDATE; fk_trigger->columns = NIL; fk_trigger->whenClause = NULL; fk_trigger->isconstraint = true; - fk_trigger->constrrel = myRel; + fk_trigger->constrrel = NULL; switch (fkconstraint->fk_upd_action) { case FKCONSTR_ACTION_NOACTION: @@ -6360,7 +6362,8 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, } fk_trigger->args = NIL; - (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true); + (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid, + indexOid, true); /* Make changes-so-far visible */ CommandCounterIncrement(); @@ -6380,8 +6383,10 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, * and the use of self-referential FKs is rare enough, that we live with * it for now. There will be a real fix in PG 9.2. */ - CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, indexOid, true); - CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, indexOid, false); + CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid, + indexOid, true); + CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid, + indexOid, false); } /* @@ -7179,7 +7184,8 @@ static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) { ObjectAddress obj; - ListCell *l; + ListCell *def_item; + ListCell *oid_item; /* * Re-parse the index and constraint definitions, and attach them to the @@ -7188,11 +7194,36 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) * lock on the table the constraint is attached to, and we need to get * that before dropping. It's safe because the parser won't actually look * at the catalogs to detect the existing entry. + * + * We can't rely on the output of deparsing to tell us which relation + * to operate on, because concurrent activity might have made the name + * resolve differently. Instead, we've got to use the OID of the + * constraint or index we're processing to figure out which relation + * to operate on. */ - foreach(l, tab->changedIndexDefs) - ATPostAlterTypeParse((char *) lfirst(l), wqueue, lockmode); - foreach(l, tab->changedConstraintDefs) - ATPostAlterTypeParse((char *) lfirst(l), wqueue, lockmode); + forboth(oid_item, tab->changedConstraintOids, + def_item, tab->changedConstraintDefs) + { + Oid oldId = lfirst_oid(oid_item); + Oid relid; + Oid confrelid; + + get_constraint_relation_oids(oldId, &relid, &confrelid); + ATPostAlterTypeParse(relid, confrelid, + (char *) lfirst(def_item), + wqueue, lockmode); + } + forboth(oid_item, tab->changedIndexOids, + def_item, tab->changedIndexDefs) + { + Oid oldId = lfirst_oid(oid_item); + Oid relid; + + relid = IndexGetRelation(oldId); + ATPostAlterTypeParse(relid, InvalidOid, + (char *) lfirst(def_item), + wqueue, lockmode); + } /* * Now we can drop the existing constraints and indexes --- constraints @@ -7202,18 +7233,18 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) * should be okay to use DROP_RESTRICT here, since nothing else should be * depending on these objects. */ - foreach(l, tab->changedConstraintOids) + foreach(oid_item, tab->changedConstraintOids) { obj.classId = ConstraintRelationId; - obj.objectId = lfirst_oid(l); + obj.objectId = lfirst_oid(oid_item); obj.objectSubId = 0; performDeletion(&obj, DROP_RESTRICT); } - foreach(l, tab->changedIndexOids) + foreach(oid_item, tab->changedIndexOids) { obj.classId = RelationRelationId; - obj.objectId = lfirst_oid(l); + obj.objectId = lfirst_oid(oid_item); obj.objectSubId = 0; performDeletion(&obj, DROP_RESTRICT); } @@ -7225,11 +7256,13 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) } static void -ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode) +ATPostAlterTypeParse(Oid oldRelId, Oid refRelId, char *cmd, List **wqueue, + LOCKMODE lockmode) { List *raw_parsetree_list; List *querytree_list; ListCell *list_item; + Relation rel; /* * We expect that we will get only ALTER TABLE and CREATE INDEX @@ -7245,16 +7278,21 @@ ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode) if (IsA(stmt, IndexStmt)) querytree_list = lappend(querytree_list, - transformIndexStmt((IndexStmt *) stmt, + transformIndexStmt(oldRelId, + (IndexStmt *) stmt, cmd)); else if (IsA(stmt, AlterTableStmt)) querytree_list = list_concat(querytree_list, - transformAlterTableStmt((AlterTableStmt *) stmt, + transformAlterTableStmt(oldRelId, + (AlterTableStmt *) stmt, cmd)); else querytree_list = lappend(querytree_list, stmt); } + /* Caller should already have acquired whatever lock we need. */ + rel = relation_open(oldRelId, NoLock); + /* * Attach each generated command to the proper place in the work queue. * Note this could result in creation of entirely new work-queue entries. @@ -7266,7 +7304,6 @@ ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode) foreach(list_item, querytree_list) { Node *stm = (Node *) lfirst(list_item); - Relation rel; AlteredTableInfo *tab; switch (nodeTag(stm)) @@ -7276,14 +7313,12 @@ ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode) IndexStmt *stmt = (IndexStmt *) stm; AlterTableCmd *newcmd; - rel = relation_openrv(stmt->relation, lockmode); tab = ATGetQueueEntry(wqueue, rel); newcmd = makeNode(AlterTableCmd); newcmd->subtype = AT_ReAddIndex; newcmd->def = (Node *) stmt; tab->subcmds[AT_PASS_OLD_INDEX] = lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd); - relation_close(rel, NoLock); break; } case T_AlterTableStmt: @@ -7291,7 +7326,6 @@ ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode) AlterTableStmt *stmt = (AlterTableStmt *) stm; ListCell *lcmd; - rel = relation_openrv(stmt->relation, lockmode); tab = ATGetQueueEntry(wqueue, rel); foreach(lcmd, stmt->cmds) { @@ -7314,7 +7348,6 @@ ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode) (int) cmd->subtype); } } - relation_close(rel, NoLock); break; } default: @@ -7322,8 +7355,9 @@ ATPostAlterTypeParse(char *cmd, List **wqueue, LOCKMODE lockmode) (int) nodeTag(stm)); } } -} + relation_close(rel, NoLock); +} /* * ALTER TABLE OWNER @@ -9002,7 +9036,8 @@ ATExecGenericOptions(Relation rel, List *options) /* * Execute ALTER TABLE SET SCHEMA * - * Note: caller must have checked ownership of the relation already + * WARNING WARNING WARNING: In previous *minor* releases the caller was + * responsible for checking ownership of the relation, but now we do it here. */ void AlterTableNamespace(RangeVar *relation, const char *newschema, @@ -9017,6 +9052,7 @@ AlterTableNamespace(RangeVar *relation, const char *newschema, rel = relation_openrv(relation, lockmode); relid = RelationGetRelid(rel); + CheckRelationOwnership(relid, true); oldNspOid = RelationGetNamespace(rel); /* Check relation type against type specified in the ALTER command */ diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index ab87562d0ce..3c2447671c7 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -92,6 +92,13 @@ static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, * queryString is the source text of the CREATE TRIGGER command. * This must be supplied if a whenClause is specified, else it can be NULL. * + * relOid, if nonzero, is the relation on which the trigger should be + * created. If zero, the name provided in the statement will be looked up. + * + * refRelOid, if nonzero, is the relation to which the constraint trigger + * refers. If zero, the constraint relation name provided in the statement + * will be looked up as needed. + * * constraintOid, if nonzero, says that this trigger is being created * internally to implement that constraint. A suitable pg_depend entry will * be made to link the trigger to that constraint. constraintOid is zero when @@ -114,7 +121,7 @@ static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, */ Oid CreateTrigger(CreateTrigStmt *stmt, const char *queryString, - Oid constraintOid, Oid indexOid, + Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, bool isInternal) { int16 tgtype; @@ -143,7 +150,10 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, ObjectAddress myself, referenced; - rel = heap_openrv(stmt->relation, AccessExclusiveLock); + if (OidIsValid(relOid)) + rel = heap_open(relOid, AccessExclusiveLock); + else + rel = heap_openrv(stmt->relation, AccessExclusiveLock); /* * Triggers must be on tables or views, and there are additional @@ -192,8 +202,13 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, errmsg("permission denied: \"%s\" is a system catalog", RelationGetRelationName(rel)))); - if (stmt->isconstraint && stmt->constrrel != NULL) - constrrelid = RangeVarGetRelid(stmt->constrrel, false); + if (stmt->isconstraint) + { + if (OidIsValid(refRelOid)) + constrrelid = refRelOid; + else if (stmt->constrrel != NULL) + constrrelid = RangeVarGetRelid(stmt->constrrel, false); + } /* permission checks */ if (!isInternal) @@ -501,7 +516,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("trigger \"%s\" for relation \"%s\" already exists", - trigname, stmt->relation->relname))); + trigname, RelationGetRelationName(rel)))); } systable_endscan(tgscan); } |