diff options
Diffstat (limited to 'meta-oe/recipes-support/postgresql/files/0005-Avoid-repeated-name-lookups-during-table-and-index-D.patch')
| -rw-r--r-- | meta-oe/recipes-support/postgresql/files/0005-Avoid-repeated-name-lookups-during-table-and-index-D.patch | 1082 |
1 files changed, 0 insertions, 1082 deletions
diff --git a/meta-oe/recipes-support/postgresql/files/0005-Avoid-repeated-name-lookups-during-table-and-index-D.patch b/meta-oe/recipes-support/postgresql/files/0005-Avoid-repeated-name-lookups-during-table-and-index-D.patch deleted file mode 100644 index f1aa212502..0000000000 --- a/meta-oe/recipes-support/postgresql/files/0005-Avoid-repeated-name-lookups-during-table-and-index-D.patch +++ /dev/null | |||
| @@ -1,1082 +0,0 @@ | |||
| 1 | From 820ab11fbfd508fc75a39c43ad2c1b3e79c4982b Mon Sep 17 00:00:00 2001 | ||
| 2 | From: Robert Haas <rhaas@postgresql.org> | ||
| 3 | Date: Mon, 17 Feb 2014 09:33:31 -0500 | ||
| 4 | Subject: [PATCH] Avoid repeated name lookups during table and index DDL. | ||
| 5 | |||
| 6 | commit 820ab11fbfd508fc75a39c43ad2c1b3e79c4982b REL9_2_STABLE | ||
| 7 | |||
| 8 | If the name lookups come to different conclusions due to concurrent | ||
| 9 | activity, we might perform some parts of the DDL on a different table | ||
| 10 | than other parts. At least in the case of CREATE INDEX, this can be | ||
| 11 | used to cause the permissions checks to be performed against a | ||
| 12 | different table than the index creation, allowing for a privilege | ||
| 13 | escalation attack. | ||
| 14 | |||
| 15 | This changes the calling convention for DefineIndex, CreateTrigger, | ||
| 16 | transformIndexStmt, transformAlterTableStmt, CheckIndexCompatible | ||
| 17 | (in 9.2 and newer), and AlterTable (in 9.1 and older). In addition, | ||
| 18 | CheckRelationOwnership is removed in 9.2 and newer and the calling | ||
| 19 | convention is changed in older branches. A field has also been added | ||
| 20 | to the Constraint node (FkConstraint in 8.4). Third-party code calling | ||
| 21 | these functions or using the Constraint node will require updating. | ||
| 22 | |||
| 23 | Report by Andres Freund. Patch by Robert Haas and Andres Freund, | ||
| 24 | reviewed by Tom Lane. | ||
| 25 | |||
| 26 | Security: CVE-2014-0062 | ||
| 27 | |||
| 28 | Upstream-Status: Backport | ||
| 29 | |||
| 30 | Signed-off-by: Kai Kang <kai.kang@windriver.com> | ||
| 31 | --- | ||
| 32 | src/backend/bootstrap/bootparse.y | 17 ++++- | ||
| 33 | src/backend/catalog/index.c | 10 +-- | ||
| 34 | src/backend/catalog/pg_constraint.c | 19 +++++ | ||
| 35 | src/backend/commands/indexcmds.c | 22 ++++-- | ||
| 36 | src/backend/commands/tablecmds.c | 137 +++++++++++++++++++++++++---------- | ||
| 37 | src/backend/commands/trigger.c | 28 ++++++-- | ||
| 38 | src/backend/nodes/copyfuncs.c | 1 + | ||
| 39 | src/backend/nodes/equalfuncs.c | 1 + | ||
| 40 | src/backend/nodes/outfuncs.c | 1 + | ||
| 41 | src/backend/parser/parse_utilcmd.c | 64 ++++++----------- | ||
| 42 | src/backend/tcop/utility.c | 73 +++++++------------ | ||
| 43 | src/include/catalog/pg_constraint.h | 1 + | ||
| 44 | src/include/commands/defrem.h | 4 +- | ||
| 45 | src/include/commands/tablecmds.h | 2 + | ||
| 46 | src/include/commands/trigger.h | 2 +- | ||
| 47 | src/include/nodes/parsenodes.h | 2 + | ||
| 48 | src/include/parser/parse_utilcmd.h | 5 +- | ||
| 49 | src/include/tcop/utility.h | 2 - | ||
| 50 | 18 files changed, 234 insertions(+), 157 deletions(-) | ||
| 51 | |||
| 52 | diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y | ||
| 53 | index f4a1b8f..eeffb0f 100644 | ||
| 54 | --- a/src/backend/bootstrap/bootparse.y | ||
| 55 | +++ b/src/backend/bootstrap/bootparse.y | ||
| 56 | @@ -27,6 +27,7 @@ | ||
| 57 | #include "bootstrap/bootstrap.h" | ||
| 58 | #include "catalog/catalog.h" | ||
| 59 | #include "catalog/heap.h" | ||
| 60 | +#include "catalog/namespace.h" | ||
| 61 | #include "catalog/pg_am.h" | ||
| 62 | #include "catalog/pg_attribute.h" | ||
| 63 | #include "catalog/pg_authid.h" | ||
| 64 | @@ -281,6 +282,7 @@ Boot_DeclareIndexStmt: | ||
| 65 | XDECLARE INDEX boot_ident oidspec ON boot_ident USING boot_ident LPAREN boot_index_params RPAREN | ||
| 66 | { | ||
| 67 | IndexStmt *stmt = makeNode(IndexStmt); | ||
| 68 | + Oid relationId; | ||
| 69 | |||
| 70 | do_start(); | ||
| 71 | |||
| 72 | @@ -302,7 +304,12 @@ Boot_DeclareIndexStmt: | ||
| 73 | stmt->initdeferred = false; | ||
| 74 | stmt->concurrent = false; | ||
| 75 | |||
| 76 | - DefineIndex(stmt, | ||
| 77 | + /* locks and races need not concern us in bootstrap mode */ | ||
| 78 | + relationId = RangeVarGetRelid(stmt->relation, NoLock, | ||
| 79 | + false); | ||
| 80 | + | ||
| 81 | + DefineIndex(relationId, | ||
| 82 | + stmt, | ||
| 83 | $4, | ||
| 84 | false, | ||
| 85 | false, | ||
| 86 | @@ -316,6 +323,7 @@ Boot_DeclareUniqueIndexStmt: | ||
| 87 | XDECLARE UNIQUE INDEX boot_ident oidspec ON boot_ident USING boot_ident LPAREN boot_index_params RPAREN | ||
| 88 | { | ||
| 89 | IndexStmt *stmt = makeNode(IndexStmt); | ||
| 90 | + Oid relationId; | ||
| 91 | |||
| 92 | do_start(); | ||
| 93 | |||
| 94 | @@ -337,7 +345,12 @@ Boot_DeclareUniqueIndexStmt: | ||
| 95 | stmt->initdeferred = false; | ||
| 96 | stmt->concurrent = false; | ||
| 97 | |||
| 98 | - DefineIndex(stmt, | ||
| 99 | + /* locks and races need not concern us in bootstrap mode */ | ||
| 100 | + relationId = RangeVarGetRelid(stmt->relation, NoLock, | ||
| 101 | + false); | ||
| 102 | + | ||
| 103 | + DefineIndex(relationId, | ||
| 104 | + stmt, | ||
| 105 | $5, | ||
| 106 | false, | ||
| 107 | false, | ||
| 108 | diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c | ||
| 109 | index 7d6346a..ca8acf3 100644 | ||
| 110 | --- a/src/backend/catalog/index.c | ||
| 111 | +++ b/src/backend/catalog/index.c | ||
| 112 | @@ -1202,18 +1202,13 @@ index_constraint_create(Relation heapRelation, | ||
| 113 | */ | ||
| 114 | if (deferrable) | ||
| 115 | { | ||
| 116 | - RangeVar *heapRel; | ||
| 117 | CreateTrigStmt *trigger; | ||
| 118 | |||
| 119 | - heapRel = makeRangeVar(get_namespace_name(namespaceId), | ||
| 120 | - pstrdup(RelationGetRelationName(heapRelation)), | ||
| 121 | - -1); | ||
| 122 | - | ||
| 123 | trigger = makeNode(CreateTrigStmt); | ||
| 124 | trigger->trigname = (constraintType == CONSTRAINT_PRIMARY) ? | ||
| 125 | "PK_ConstraintTrigger" : | ||
| 126 | "Unique_ConstraintTrigger"; | ||
| 127 | - trigger->relation = heapRel; | ||
| 128 | + trigger->relation = NULL; | ||
| 129 | trigger->funcname = SystemFuncName("unique_key_recheck"); | ||
| 130 | trigger->args = NIL; | ||
| 131 | trigger->row = true; | ||
| 132 | @@ -1226,7 +1221,8 @@ index_constraint_create(Relation heapRelation, | ||
| 133 | trigger->initdeferred = initdeferred; | ||
| 134 | trigger->constrrel = NULL; | ||
| 135 | |||
| 136 | - (void) CreateTrigger(trigger, NULL, conOid, indexRelationId, true); | ||
| 137 | + (void) CreateTrigger(trigger, NULL, RelationGetRelid(heapRelation), | ||
| 138 | + InvalidOid, conOid, indexRelationId, true); | ||
| 139 | } | ||
| 140 | |||
| 141 | /* | ||
| 142 | diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c | ||
| 143 | index 107a780..08a94cf 100644 | ||
| 144 | --- a/src/backend/catalog/pg_constraint.c | ||
| 145 | +++ b/src/backend/catalog/pg_constraint.c | ||
| 146 | @@ -746,6 +746,25 @@ AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, | ||
| 147 | } | ||
| 148 | |||
| 149 | /* | ||
| 150 | + * get_constraint_relation_oids | ||
| 151 | + * Find the IDs of the relations to which a constraint refers. | ||
| 152 | + */ | ||
| 153 | +void | ||
| 154 | +get_constraint_relation_oids(Oid constraint_oid, Oid *conrelid, Oid *confrelid) | ||
| 155 | +{ | ||
| 156 | + HeapTuple tup; | ||
| 157 | + Form_pg_constraint con; | ||
| 158 | + | ||
| 159 | + tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraint_oid)); | ||
| 160 | + if (!HeapTupleIsValid(tup)) /* should not happen */ | ||
| 161 | + elog(ERROR, "cache lookup failed for constraint %u", constraint_oid); | ||
| 162 | + con = (Form_pg_constraint) GETSTRUCT(tup); | ||
| 163 | + *conrelid = con->conrelid; | ||
| 164 | + *confrelid = con->confrelid; | ||
| 165 | + ReleaseSysCache(tup); | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +/* | ||
| 169 | * get_relation_constraint_oid | ||
| 170 | * Find a constraint on the specified relation with the specified name. | ||
| 171 | * Returns constraint's OID. | ||
| 172 | diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c | ||
| 173 | index f3ee278..ec5fb0d 100644 | ||
| 174 | --- a/src/backend/commands/indexcmds.c | ||
| 175 | +++ b/src/backend/commands/indexcmds.c | ||
| 176 | @@ -111,7 +111,6 @@ static void RangeVarCallbackForReindexIndex(const RangeVar *relation, | ||
| 177 | */ | ||
| 178 | bool | ||
| 179 | CheckIndexCompatible(Oid oldId, | ||
| 180 | - RangeVar *heapRelation, | ||
| 181 | char *accessMethodName, | ||
| 182 | List *attributeList, | ||
| 183 | List *exclusionOpNames) | ||
| 184 | @@ -139,7 +138,7 @@ CheckIndexCompatible(Oid oldId, | ||
| 185 | Datum d; | ||
| 186 | |||
| 187 | /* Caller should already have the relation locked in some way. */ | ||
| 188 | - relationId = RangeVarGetRelid(heapRelation, NoLock, false); | ||
| 189 | + relationId = IndexGetRelation(oldId, false); | ||
| 190 | |||
| 191 | /* | ||
| 192 | * We can pretend isconstraint = false unconditionally. It only serves to | ||
| 193 | @@ -279,6 +278,8 @@ CheckIndexCompatible(Oid oldId, | ||
| 194 | * DefineIndex | ||
| 195 | * Creates a new index. | ||
| 196 | * | ||
| 197 | + * 'relationId': the OID of the heap relation on which the index is to be | ||
| 198 | + * created | ||
| 199 | * 'stmt': IndexStmt describing the properties of the new index. | ||
| 200 | * 'indexRelationId': normally InvalidOid, but during bootstrap can be | ||
| 201 | * nonzero to specify a preselected OID for the index. | ||
| 202 | @@ -292,7 +293,8 @@ CheckIndexCompatible(Oid oldId, | ||
| 203 | * Returns the OID of the created index. | ||
| 204 | */ | ||
| 205 | Oid | ||
| 206 | -DefineIndex(IndexStmt *stmt, | ||
| 207 | +DefineIndex(Oid relationId, | ||
| 208 | + IndexStmt *stmt, | ||
| 209 | Oid indexRelationId, | ||
| 210 | bool is_alter_table, | ||
| 211 | bool check_rights, | ||
| 212 | @@ -305,7 +307,6 @@ DefineIndex(IndexStmt *stmt, | ||
| 213 | Oid *collationObjectId; | ||
| 214 | Oid *classObjectId; | ||
| 215 | Oid accessMethodId; | ||
| 216 | - Oid relationId; | ||
| 217 | Oid namespaceId; | ||
| 218 | Oid tablespaceId; | ||
| 219 | List *indexColNames; | ||
| 220 | @@ -325,6 +326,7 @@ DefineIndex(IndexStmt *stmt, | ||
| 221 | int n_old_snapshots; | ||
| 222 | LockRelId heaprelid; | ||
| 223 | LOCKTAG heaplocktag; | ||
| 224 | + LOCKMODE lockmode; | ||
| 225 | Snapshot snapshot; | ||
| 226 | int i; | ||
| 227 | |||
| 228 | @@ -343,14 +345,18 @@ DefineIndex(IndexStmt *stmt, | ||
| 229 | INDEX_MAX_KEYS))); | ||
| 230 | |||
| 231 | /* | ||
| 232 | - * Open heap relation, acquire a suitable lock on it, remember its OID | ||
| 233 | - * | ||
| 234 | * Only SELECT ... FOR UPDATE/SHARE are allowed while doing a standard | ||
| 235 | * index build; but for concurrent builds we allow INSERT/UPDATE/DELETE | ||
| 236 | * (but not VACUUM). | ||
| 237 | + * | ||
| 238 | + * NB: Caller is responsible for making sure that relationId refers | ||
| 239 | + * to the relation on which the index should be built; except in bootstrap | ||
| 240 | + * mode, this will typically require the caller to have already locked | ||
| 241 | + * the relation. To avoid lock upgrade hazards, that lock should be at | ||
| 242 | + * least as strong as the one we take here. | ||
| 243 | */ | ||
| 244 | - rel = heap_openrv(stmt->relation, | ||
| 245 | - (stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock)); | ||
| 246 | + lockmode = stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock; | ||
| 247 | + rel = heap_open(relationId, lockmode); | ||
| 248 | |||
| 249 | relationId = RelationGetRelid(rel); | ||
| 250 | namespaceId = RelationGetNamespace(rel); | ||
| 251 | diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c | ||
| 252 | index 7c1f779..bcb81ea 100644 | ||
| 253 | --- a/src/backend/commands/tablecmds.c | ||
| 254 | +++ b/src/backend/commands/tablecmds.c | ||
| 255 | @@ -283,7 +283,8 @@ static void validateCheckConstraint(Relation rel, HeapTuple constrtup); | ||
| 256 | static void validateForeignKeyConstraint(char *conname, | ||
| 257 | Relation rel, Relation pkrel, | ||
| 258 | Oid pkindOid, Oid constraintOid); | ||
| 259 | -static void createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 260 | +static void createForeignKeyTriggers(Relation rel, Oid refRelOid, | ||
| 261 | + Constraint *fkconstraint, | ||
| 262 | Oid constraintOid, Oid indexOid); | ||
| 263 | static void ATController(Relation rel, List *cmds, bool recurse, LOCKMODE lockmode); | ||
| 264 | static void ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd, | ||
| 265 | @@ -360,8 +361,9 @@ static void ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel, | ||
| 266 | static void ATExecAlterColumnGenericOptions(Relation rel, const char *colName, | ||
| 267 | List *options, LOCKMODE lockmode); | ||
| 268 | static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode); | ||
| 269 | -static void ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 270 | - List **wqueue, LOCKMODE lockmode, bool rewrite); | ||
| 271 | +static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, | ||
| 272 | + char *cmd, List **wqueue, LOCKMODE lockmode, | ||
| 273 | + bool rewrite); | ||
| 274 | static void TryReuseIndex(Oid oldId, IndexStmt *stmt); | ||
| 275 | static void TryReuseForeignKey(Oid oldId, Constraint *con); | ||
| 276 | static void change_owner_fix_column_acls(Oid relationOid, | ||
| 277 | @@ -5406,7 +5408,8 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel, | ||
| 278 | |||
| 279 | /* The IndexStmt has already been through transformIndexStmt */ | ||
| 280 | |||
| 281 | - new_index = DefineIndex(stmt, | ||
| 282 | + new_index = DefineIndex(RelationGetRelid(rel), | ||
| 283 | + stmt, | ||
| 284 | InvalidOid, /* no predefined OID */ | ||
| 285 | true, /* is_alter_table */ | ||
| 286 | check_rights, | ||
| 287 | @@ -5728,7 +5731,10 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, | ||
| 288 | * table; trying to start with a lesser lock will just create a risk of | ||
| 289 | * deadlock.) | ||
| 290 | */ | ||
| 291 | - pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock); | ||
| 292 | + if (OidIsValid(fkconstraint->old_pktable_oid)) | ||
| 293 | + pkrel = heap_open(fkconstraint->old_pktable_oid, AccessExclusiveLock); | ||
| 294 | + else | ||
| 295 | + pkrel = heap_openrv(fkconstraint->pktable, AccessExclusiveLock); | ||
| 296 | |||
| 297 | /* | ||
| 298 | * Validity checks (permission checks wait till we have the column | ||
| 299 | @@ -6066,7 +6072,8 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel, | ||
| 300 | /* | ||
| 301 | * Create the triggers that will enforce the constraint. | ||
| 302 | */ | ||
| 303 | - createForeignKeyTriggers(rel, fkconstraint, constrOid, indexOid); | ||
| 304 | + createForeignKeyTriggers(rel, RelationGetRelid(pkrel), fkconstraint, | ||
| 305 | + constrOid, indexOid); | ||
| 306 | |||
| 307 | /* | ||
| 308 | * Tell Phase 3 to check that the constraint is satisfied by existing | ||
| 309 | @@ -6736,7 +6743,7 @@ validateForeignKeyConstraint(char *conname, | ||
| 310 | } | ||
| 311 | |||
| 312 | static void | ||
| 313 | -CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint, | ||
| 314 | +CreateFKCheckTrigger(Oid myRelOid, Oid refRelOid, Constraint *fkconstraint, | ||
| 315 | Oid constraintOid, Oid indexOid, bool on_insert) | ||
| 316 | { | ||
| 317 | CreateTrigStmt *fk_trigger; | ||
| 318 | @@ -6752,7 +6759,7 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint, | ||
| 319 | */ | ||
| 320 | fk_trigger = makeNode(CreateTrigStmt); | ||
| 321 | fk_trigger->trigname = "RI_ConstraintTrigger_c"; | ||
| 322 | - fk_trigger->relation = myRel; | ||
| 323 | + fk_trigger->relation = NULL; | ||
| 324 | fk_trigger->row = true; | ||
| 325 | fk_trigger->timing = TRIGGER_TYPE_AFTER; | ||
| 326 | |||
| 327 | @@ -6773,10 +6780,11 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint, | ||
| 328 | fk_trigger->isconstraint = true; | ||
| 329 | fk_trigger->deferrable = fkconstraint->deferrable; | ||
| 330 | fk_trigger->initdeferred = fkconstraint->initdeferred; | ||
| 331 | - fk_trigger->constrrel = fkconstraint->pktable; | ||
| 332 | + fk_trigger->constrrel = NULL; | ||
| 333 | fk_trigger->args = NIL; | ||
| 334 | |||
| 335 | - (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true); | ||
| 336 | + (void) CreateTrigger(fk_trigger, NULL, myRelOid, refRelOid, constraintOid, | ||
| 337 | + indexOid, true); | ||
| 338 | |||
| 339 | /* Make changes-so-far visible */ | ||
| 340 | CommandCounterIncrement(); | ||
| 341 | @@ -6786,18 +6794,13 @@ CreateFKCheckTrigger(RangeVar *myRel, Constraint *fkconstraint, | ||
| 342 | * Create the triggers that implement an FK constraint. | ||
| 343 | */ | ||
| 344 | static void | ||
| 345 | -createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 346 | +createForeignKeyTriggers(Relation rel, Oid refRelOid, Constraint *fkconstraint, | ||
| 347 | Oid constraintOid, Oid indexOid) | ||
| 348 | { | ||
| 349 | - RangeVar *myRel; | ||
| 350 | + Oid myRelOid; | ||
| 351 | CreateTrigStmt *fk_trigger; | ||
| 352 | |||
| 353 | - /* | ||
| 354 | - * Reconstruct a RangeVar for my relation (not passed in, unfortunately). | ||
| 355 | - */ | ||
| 356 | - myRel = makeRangeVar(get_namespace_name(RelationGetNamespace(rel)), | ||
| 357 | - pstrdup(RelationGetRelationName(rel)), | ||
| 358 | - -1); | ||
| 359 | + myRelOid = RelationGetRelid(rel); | ||
| 360 | |||
| 361 | /* Make changes-so-far visible */ | ||
| 362 | CommandCounterIncrement(); | ||
| 363 | @@ -6808,14 +6811,14 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 364 | */ | ||
| 365 | fk_trigger = makeNode(CreateTrigStmt); | ||
| 366 | fk_trigger->trigname = "RI_ConstraintTrigger_a"; | ||
| 367 | - fk_trigger->relation = fkconstraint->pktable; | ||
| 368 | + fk_trigger->relation = NULL; | ||
| 369 | fk_trigger->row = true; | ||
| 370 | fk_trigger->timing = TRIGGER_TYPE_AFTER; | ||
| 371 | fk_trigger->events = TRIGGER_TYPE_DELETE; | ||
| 372 | fk_trigger->columns = NIL; | ||
| 373 | fk_trigger->whenClause = NULL; | ||
| 374 | fk_trigger->isconstraint = true; | ||
| 375 | - fk_trigger->constrrel = myRel; | ||
| 376 | + fk_trigger->constrrel = NULL; | ||
| 377 | switch (fkconstraint->fk_del_action) | ||
| 378 | { | ||
| 379 | case FKCONSTR_ACTION_NOACTION: | ||
| 380 | @@ -6850,7 +6853,8 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 381 | } | ||
| 382 | fk_trigger->args = NIL; | ||
| 383 | |||
| 384 | - (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true); | ||
| 385 | + (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid, | ||
| 386 | + indexOid, true); | ||
| 387 | |||
| 388 | /* Make changes-so-far visible */ | ||
| 389 | CommandCounterIncrement(); | ||
| 390 | @@ -6861,14 +6865,14 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 391 | */ | ||
| 392 | fk_trigger = makeNode(CreateTrigStmt); | ||
| 393 | fk_trigger->trigname = "RI_ConstraintTrigger_a"; | ||
| 394 | - fk_trigger->relation = fkconstraint->pktable; | ||
| 395 | + fk_trigger->relation = NULL; | ||
| 396 | fk_trigger->row = true; | ||
| 397 | fk_trigger->timing = TRIGGER_TYPE_AFTER; | ||
| 398 | fk_trigger->events = TRIGGER_TYPE_UPDATE; | ||
| 399 | fk_trigger->columns = NIL; | ||
| 400 | fk_trigger->whenClause = NULL; | ||
| 401 | fk_trigger->isconstraint = true; | ||
| 402 | - fk_trigger->constrrel = myRel; | ||
| 403 | + fk_trigger->constrrel = NULL; | ||
| 404 | switch (fkconstraint->fk_upd_action) | ||
| 405 | { | ||
| 406 | case FKCONSTR_ACTION_NOACTION: | ||
| 407 | @@ -6903,7 +6907,8 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 408 | } | ||
| 409 | fk_trigger->args = NIL; | ||
| 410 | |||
| 411 | - (void) CreateTrigger(fk_trigger, NULL, constraintOid, indexOid, true); | ||
| 412 | + (void) CreateTrigger(fk_trigger, NULL, refRelOid, myRelOid, constraintOid, | ||
| 413 | + indexOid, true); | ||
| 414 | |||
| 415 | /* Make changes-so-far visible */ | ||
| 416 | CommandCounterIncrement(); | ||
| 417 | @@ -6912,8 +6917,10 @@ createForeignKeyTriggers(Relation rel, Constraint *fkconstraint, | ||
| 418 | * Build and execute CREATE CONSTRAINT TRIGGER statements for the CHECK | ||
| 419 | * action for both INSERTs and UPDATEs on the referencing table. | ||
| 420 | */ | ||
| 421 | - CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, indexOid, true); | ||
| 422 | - CreateFKCheckTrigger(myRel, fkconstraint, constraintOid, indexOid, false); | ||
| 423 | + CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid, | ||
| 424 | + indexOid, true); | ||
| 425 | + CreateFKCheckTrigger(myRelOid, refRelOid, fkconstraint, constraintOid, | ||
| 426 | + indexOid, false); | ||
| 427 | } | ||
| 428 | |||
| 429 | /* | ||
| 430 | @@ -7832,15 +7839,36 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) | ||
| 431 | * lock on the table the constraint is attached to, and we need to get | ||
| 432 | * that before dropping. It's safe because the parser won't actually look | ||
| 433 | * at the catalogs to detect the existing entry. | ||
| 434 | + * | ||
| 435 | + * We can't rely on the output of deparsing to tell us which relation | ||
| 436 | + * to operate on, because concurrent activity might have made the name | ||
| 437 | + * resolve differently. Instead, we've got to use the OID of the | ||
| 438 | + * constraint or index we're processing to figure out which relation | ||
| 439 | + * to operate on. | ||
| 440 | */ | ||
| 441 | forboth(oid_item, tab->changedConstraintOids, | ||
| 442 | def_item, tab->changedConstraintDefs) | ||
| 443 | - ATPostAlterTypeParse(lfirst_oid(oid_item), (char *) lfirst(def_item), | ||
| 444 | + { | ||
| 445 | + Oid oldId = lfirst_oid(oid_item); | ||
| 446 | + Oid relid; | ||
| 447 | + Oid confrelid; | ||
| 448 | + | ||
| 449 | + get_constraint_relation_oids(oldId, &relid, &confrelid); | ||
| 450 | + ATPostAlterTypeParse(oldId, relid, confrelid, | ||
| 451 | + (char *) lfirst(def_item), | ||
| 452 | wqueue, lockmode, tab->rewrite); | ||
| 453 | + } | ||
| 454 | forboth(oid_item, tab->changedIndexOids, | ||
| 455 | def_item, tab->changedIndexDefs) | ||
| 456 | - ATPostAlterTypeParse(lfirst_oid(oid_item), (char *) lfirst(def_item), | ||
| 457 | + { | ||
| 458 | + Oid oldId = lfirst_oid(oid_item); | ||
| 459 | + Oid relid; | ||
| 460 | + | ||
| 461 | + relid = IndexGetRelation(oldId, false); | ||
| 462 | + ATPostAlterTypeParse(oldId, relid, InvalidOid, | ||
| 463 | + (char *) lfirst(def_item), | ||
| 464 | wqueue, lockmode, tab->rewrite); | ||
| 465 | + } | ||
| 466 | |||
| 467 | /* | ||
| 468 | * Now we can drop the existing constraints and indexes --- constraints | ||
| 469 | @@ -7873,12 +7901,13 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode) | ||
| 470 | } | ||
| 471 | |||
| 472 | static void | ||
| 473 | -ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 474 | +ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd, | ||
| 475 | List **wqueue, LOCKMODE lockmode, bool rewrite) | ||
| 476 | { | ||
| 477 | List *raw_parsetree_list; | ||
| 478 | List *querytree_list; | ||
| 479 | ListCell *list_item; | ||
| 480 | + Relation rel; | ||
| 481 | |||
| 482 | /* | ||
| 483 | * We expect that we will get only ALTER TABLE and CREATE INDEX | ||
| 484 | @@ -7894,16 +7923,21 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 485 | |||
| 486 | if (IsA(stmt, IndexStmt)) | ||
| 487 | querytree_list = lappend(querytree_list, | ||
| 488 | - transformIndexStmt((IndexStmt *) stmt, | ||
| 489 | + transformIndexStmt(oldRelId, | ||
| 490 | + (IndexStmt *) stmt, | ||
| 491 | cmd)); | ||
| 492 | else if (IsA(stmt, AlterTableStmt)) | ||
| 493 | querytree_list = list_concat(querytree_list, | ||
| 494 | - transformAlterTableStmt((AlterTableStmt *) stmt, | ||
| 495 | + transformAlterTableStmt(oldRelId, | ||
| 496 | + (AlterTableStmt *) stmt, | ||
| 497 | cmd)); | ||
| 498 | else | ||
| 499 | querytree_list = lappend(querytree_list, stmt); | ||
| 500 | } | ||
| 501 | |||
| 502 | + /* Caller should already have acquired whatever lock we need. */ | ||
| 503 | + rel = relation_open(oldRelId, NoLock); | ||
| 504 | + | ||
| 505 | /* | ||
| 506 | * Attach each generated command to the proper place in the work queue. | ||
| 507 | * Note this could result in creation of entirely new work-queue entries. | ||
| 508 | @@ -7915,7 +7949,6 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 509 | foreach(list_item, querytree_list) | ||
| 510 | { | ||
| 511 | Node *stm = (Node *) lfirst(list_item); | ||
| 512 | - Relation rel; | ||
| 513 | AlteredTableInfo *tab; | ||
| 514 | |||
| 515 | switch (nodeTag(stm)) | ||
| 516 | @@ -7928,14 +7961,12 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 517 | if (!rewrite) | ||
| 518 | TryReuseIndex(oldId, stmt); | ||
| 519 | |||
| 520 | - rel = relation_openrv(stmt->relation, lockmode); | ||
| 521 | tab = ATGetQueueEntry(wqueue, rel); | ||
| 522 | newcmd = makeNode(AlterTableCmd); | ||
| 523 | newcmd->subtype = AT_ReAddIndex; | ||
| 524 | newcmd->def = (Node *) stmt; | ||
| 525 | tab->subcmds[AT_PASS_OLD_INDEX] = | ||
| 526 | lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd); | ||
| 527 | - relation_close(rel, NoLock); | ||
| 528 | break; | ||
| 529 | } | ||
| 530 | case T_AlterTableStmt: | ||
| 531 | @@ -7943,7 +7974,6 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 532 | AlterTableStmt *stmt = (AlterTableStmt *) stm; | ||
| 533 | ListCell *lcmd; | ||
| 534 | |||
| 535 | - rel = relation_openrv(stmt->relation, lockmode); | ||
| 536 | tab = ATGetQueueEntry(wqueue, rel); | ||
| 537 | foreach(lcmd, stmt->cmds) | ||
| 538 | { | ||
| 539 | @@ -7964,6 +7994,7 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 540 | case AT_AddConstraint: | ||
| 541 | Assert(IsA(cmd->def, Constraint)); | ||
| 542 | con = (Constraint *) cmd->def; | ||
| 543 | + con->old_pktable_oid = refRelId; | ||
| 544 | /* rewriting neither side of a FK */ | ||
| 545 | if (con->contype == CONSTR_FOREIGN && | ||
| 546 | !rewrite && !tab->rewrite) | ||
| 547 | @@ -7977,7 +8008,6 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 548 | (int) cmd->subtype); | ||
| 549 | } | ||
| 550 | } | ||
| 551 | - relation_close(rel, NoLock); | ||
| 552 | break; | ||
| 553 | } | ||
| 554 | default: | ||
| 555 | @@ -7985,6 +8015,8 @@ ATPostAlterTypeParse(Oid oldId, char *cmd, | ||
| 556 | (int) nodeTag(stm)); | ||
| 557 | } | ||
| 558 | } | ||
| 559 | + | ||
| 560 | + relation_close(rel, NoLock); | ||
| 561 | } | ||
| 562 | |||
| 563 | /* | ||
| 564 | @@ -7995,7 +8027,6 @@ static void | ||
| 565 | TryReuseIndex(Oid oldId, IndexStmt *stmt) | ||
| 566 | { | ||
| 567 | if (CheckIndexCompatible(oldId, | ||
| 568 | - stmt->relation, | ||
| 569 | stmt->accessMethod, | ||
| 570 | stmt->indexParams, | ||
| 571 | stmt->excludeOpNames)) | ||
| 572 | @@ -10291,6 +10322,38 @@ RangeVarCallbackOwnsTable(const RangeVar *relation, | ||
| 573 | } | ||
| 574 | |||
| 575 | /* | ||
| 576 | + * Callback to RangeVarGetRelidExtended(), similar to | ||
| 577 | + * RangeVarCallbackOwnsTable() but without checks on the type of the relation. | ||
| 578 | + */ | ||
| 579 | +void | ||
| 580 | +RangeVarCallbackOwnsRelation(const RangeVar *relation, | ||
| 581 | + Oid relId, Oid oldRelId, void *arg) | ||
| 582 | +{ | ||
| 583 | + HeapTuple tuple; | ||
| 584 | + | ||
| 585 | + /* Nothing to do if the relation was not found. */ | ||
| 586 | + if (!OidIsValid(relId)) | ||
| 587 | + return; | ||
| 588 | + | ||
| 589 | + tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relId)); | ||
| 590 | + if (!HeapTupleIsValid(tuple)) /* should not happen */ | ||
| 591 | + elog(ERROR, "cache lookup failed for relation %u", relId); | ||
| 592 | + | ||
| 593 | + if (!pg_class_ownercheck(relId, GetUserId())) | ||
| 594 | + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, | ||
| 595 | + relation->relname); | ||
| 596 | + | ||
| 597 | + if (!allowSystemTableMods && | ||
| 598 | + IsSystemClass((Form_pg_class) GETSTRUCT(tuple))) | ||
| 599 | + ereport(ERROR, | ||
| 600 | + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | ||
| 601 | + errmsg("permission denied: \"%s\" is a system catalog", | ||
| 602 | + relation->relname))); | ||
| 603 | + | ||
| 604 | + ReleaseSysCache(tuple); | ||
| 605 | +} | ||
| 606 | + | ||
| 607 | +/* | ||
| 608 | * Common RangeVarGetRelid callback for rename, set schema, and alter table | ||
| 609 | * processing. | ||
| 610 | */ | ||
| 611 | diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c | ||
| 612 | index f546d94..9e6c954 100644 | ||
| 613 | --- a/src/backend/commands/trigger.c | ||
| 614 | +++ b/src/backend/commands/trigger.c | ||
| 615 | @@ -42,6 +42,7 @@ | ||
| 616 | #include "pgstat.h" | ||
| 617 | #include "rewrite/rewriteManip.h" | ||
| 618 | #include "storage/bufmgr.h" | ||
| 619 | +#include "storage/lmgr.h" | ||
| 620 | #include "tcop/utility.h" | ||
| 621 | #include "utils/acl.h" | ||
| 622 | #include "utils/builtins.h" | ||
| 623 | @@ -94,6 +95,13 @@ static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, | ||
| 624 | * queryString is the source text of the CREATE TRIGGER command. | ||
| 625 | * This must be supplied if a whenClause is specified, else it can be NULL. | ||
| 626 | * | ||
| 627 | + * relOid, if nonzero, is the relation on which the trigger should be | ||
| 628 | + * created. If zero, the name provided in the statement will be looked up. | ||
| 629 | + * | ||
| 630 | + * refRelOid, if nonzero, is the relation to which the constraint trigger | ||
| 631 | + * refers. If zero, the constraint relation name provided in the statement | ||
| 632 | + * will be looked up as needed. | ||
| 633 | + * | ||
| 634 | * constraintOid, if nonzero, says that this trigger is being created | ||
| 635 | * internally to implement that constraint. A suitable pg_depend entry will | ||
| 636 | * be made to link the trigger to that constraint. constraintOid is zero when | ||
| 637 | @@ -116,7 +124,7 @@ static void AfterTriggerSaveEvent(EState *estate, ResultRelInfo *relinfo, | ||
| 638 | */ | ||
| 639 | Oid | ||
| 640 | CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 641 | - Oid constraintOid, Oid indexOid, | ||
| 642 | + Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, | ||
| 643 | bool isInternal) | ||
| 644 | { | ||
| 645 | int16 tgtype; | ||
| 646 | @@ -145,7 +153,10 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 647 | ObjectAddress myself, | ||
| 648 | referenced; | ||
| 649 | |||
| 650 | - rel = heap_openrv(stmt->relation, AccessExclusiveLock); | ||
| 651 | + if (OidIsValid(relOid)) | ||
| 652 | + rel = heap_open(relOid, AccessExclusiveLock); | ||
| 653 | + else | ||
| 654 | + rel = heap_openrv(stmt->relation, AccessExclusiveLock); | ||
| 655 | |||
| 656 | /* | ||
| 657 | * Triggers must be on tables or views, and there are additional | ||
| 658 | @@ -194,7 +205,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 659 | errmsg("permission denied: \"%s\" is a system catalog", | ||
| 660 | RelationGetRelationName(rel)))); | ||
| 661 | |||
| 662 | - if (stmt->isconstraint && stmt->constrrel != NULL) | ||
| 663 | + if (stmt->isconstraint) | ||
| 664 | { | ||
| 665 | /* | ||
| 666 | * We must take a lock on the target relation to protect against | ||
| 667 | @@ -203,7 +214,14 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 668 | * might end up creating a pg_constraint entry referencing a | ||
| 669 | * nonexistent table. | ||
| 670 | */ | ||
| 671 | - constrrelid = RangeVarGetRelid(stmt->constrrel, AccessShareLock, false); | ||
| 672 | + if (OidIsValid(refRelOid)) | ||
| 673 | + { | ||
| 674 | + LockRelationOid(refRelOid, AccessShareLock); | ||
| 675 | + constrrelid = refRelOid; | ||
| 676 | + } | ||
| 677 | + else if (stmt->constrrel != NULL) | ||
| 678 | + constrrelid = RangeVarGetRelid(stmt->constrrel, AccessShareLock, | ||
| 679 | + false); | ||
| 680 | } | ||
| 681 | |||
| 682 | /* permission checks */ | ||
| 683 | @@ -513,7 +531,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 684 | ereport(ERROR, | ||
| 685 | (errcode(ERRCODE_DUPLICATE_OBJECT), | ||
| 686 | errmsg("trigger \"%s\" for relation \"%s\" already exists", | ||
| 687 | - trigname, stmt->relation->relname))); | ||
| 688 | + trigname, RelationGetRelationName(rel)))); | ||
| 689 | } | ||
| 690 | systable_endscan(tgscan); | ||
| 691 | } | ||
| 692 | diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c | ||
| 693 | index 9bac994..dbe0f6a 100644 | ||
| 694 | --- a/src/backend/nodes/copyfuncs.c | ||
| 695 | +++ b/src/backend/nodes/copyfuncs.c | ||
| 696 | @@ -2357,6 +2357,7 @@ _copyConstraint(const Constraint *from) | ||
| 697 | COPY_SCALAR_FIELD(fk_upd_action); | ||
| 698 | COPY_SCALAR_FIELD(fk_del_action); | ||
| 699 | COPY_NODE_FIELD(old_conpfeqop); | ||
| 700 | + COPY_SCALAR_FIELD(old_pktable_oid); | ||
| 701 | COPY_SCALAR_FIELD(skip_validation); | ||
| 702 | COPY_SCALAR_FIELD(initially_valid); | ||
| 703 | |||
| 704 | diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c | ||
| 705 | index d185654..f8770b0 100644 | ||
| 706 | --- a/src/backend/nodes/equalfuncs.c | ||
| 707 | +++ b/src/backend/nodes/equalfuncs.c | ||
| 708 | @@ -2143,6 +2143,7 @@ _equalConstraint(const Constraint *a, const Constraint *b) | ||
| 709 | COMPARE_SCALAR_FIELD(fk_upd_action); | ||
| 710 | COMPARE_SCALAR_FIELD(fk_del_action); | ||
| 711 | COMPARE_NODE_FIELD(old_conpfeqop); | ||
| 712 | + COMPARE_SCALAR_FIELD(old_pktable_oid); | ||
| 713 | COMPARE_SCALAR_FIELD(skip_validation); | ||
| 714 | COMPARE_SCALAR_FIELD(initially_valid); | ||
| 715 | |||
| 716 | diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c | ||
| 717 | index 1df71f6..888ffd2 100644 | ||
| 718 | --- a/src/backend/nodes/outfuncs.c | ||
| 719 | +++ b/src/backend/nodes/outfuncs.c | ||
| 720 | @@ -2653,6 +2653,7 @@ _outConstraint(StringInfo str, const Constraint *node) | ||
| 721 | WRITE_CHAR_FIELD(fk_upd_action); | ||
| 722 | WRITE_CHAR_FIELD(fk_del_action); | ||
| 723 | WRITE_NODE_FIELD(old_conpfeqop); | ||
| 724 | + WRITE_OID_FIELD(old_pktable_oid); | ||
| 725 | WRITE_BOOL_FIELD(skip_validation); | ||
| 726 | WRITE_BOOL_FIELD(initially_valid); | ||
| 727 | break; | ||
| 728 | diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c | ||
| 729 | index e3f9c62..5df939a 100644 | ||
| 730 | --- a/src/backend/parser/parse_utilcmd.c | ||
| 731 | +++ b/src/backend/parser/parse_utilcmd.c | ||
| 732 | @@ -1867,14 +1867,18 @@ transformFKConstraints(CreateStmtContext *cxt, | ||
| 733 | * a predicate expression. There are several code paths that create indexes | ||
| 734 | * without bothering to call this, because they know they don't have any | ||
| 735 | * such expressions to deal with. | ||
| 736 | + * | ||
| 737 | + * To avoid race conditions, it's important that this function rely only on | ||
| 738 | + * the passed-in relid (and not on stmt->relation) to determine the target | ||
| 739 | + * relation. | ||
| 740 | */ | ||
| 741 | IndexStmt * | ||
| 742 | -transformIndexStmt(IndexStmt *stmt, const char *queryString) | ||
| 743 | +transformIndexStmt(Oid relid, IndexStmt *stmt, const char *queryString) | ||
| 744 | { | ||
| 745 | - Relation rel; | ||
| 746 | ParseState *pstate; | ||
| 747 | RangeTblEntry *rte; | ||
| 748 | ListCell *l; | ||
| 749 | + Relation rel; | ||
| 750 | |||
| 751 | /* | ||
| 752 | * We must not scribble on the passed-in IndexStmt, so copy it. (This is | ||
| 753 | @@ -1882,26 +1886,17 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString) | ||
| 754 | */ | ||
| 755 | stmt = (IndexStmt *) copyObject(stmt); | ||
| 756 | |||
| 757 | - /* | ||
| 758 | - * Open the parent table with appropriate locking. We must do this | ||
| 759 | - * because addRangeTableEntry() would acquire only AccessShareLock, | ||
| 760 | - * leaving DefineIndex() needing to do a lock upgrade with consequent risk | ||
| 761 | - * of deadlock. Make sure this stays in sync with the type of lock | ||
| 762 | - * DefineIndex() wants. If we are being called by ALTER TABLE, we will | ||
| 763 | - * already hold a higher lock. | ||
| 764 | - */ | ||
| 765 | - rel = heap_openrv(stmt->relation, | ||
| 766 | - (stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock)); | ||
| 767 | - | ||
| 768 | /* Set up pstate */ | ||
| 769 | pstate = make_parsestate(NULL); | ||
| 770 | pstate->p_sourcetext = queryString; | ||
| 771 | |||
| 772 | /* | ||
| 773 | * Put the parent table into the rtable so that the expressions can refer | ||
| 774 | - * to its fields without qualification. | ||
| 775 | + * to its fields without qualification. Caller is responsible for locking | ||
| 776 | + * relation, but we still need to open it. | ||
| 777 | */ | ||
| 778 | - rte = addRangeTableEntry(pstate, stmt->relation, NULL, false, true); | ||
| 779 | + rel = relation_open(relid, NoLock); | ||
| 780 | + rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true); | ||
| 781 | |||
| 782 | /* no to join list, yes to namespaces */ | ||
| 783 | addRTEtoQuery(pstate, rte, false, true, true); | ||
| 784 | @@ -1955,7 +1950,7 @@ transformIndexStmt(IndexStmt *stmt, const char *queryString) | ||
| 785 | |||
| 786 | free_parsestate(pstate); | ||
| 787 | |||
| 788 | - /* Close relation, but keep the lock */ | ||
| 789 | + /* Close relation */ | ||
| 790 | heap_close(rel, NoLock); | ||
| 791 | |||
| 792 | return stmt; | ||
| 793 | @@ -2277,9 +2272,14 @@ transformRuleStmt(RuleStmt *stmt, const char *queryString, | ||
| 794 | * Returns a List of utility commands to be done in sequence. One of these | ||
| 795 | * will be the transformed AlterTableStmt, but there may be additional actions | ||
| 796 | * to be done before and after the actual AlterTable() call. | ||
| 797 | + * | ||
| 798 | + * To avoid race conditions, it's important that this function rely only on | ||
| 799 | + * the passed-in relid (and not on stmt->relation) to determine the target | ||
| 800 | + * relation. | ||
| 801 | */ | ||
| 802 | List * | ||
| 803 | -transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) | ||
| 804 | +transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, | ||
| 805 | + const char *queryString) | ||
| 806 | { | ||
| 807 | Relation rel; | ||
| 808 | ParseState *pstate; | ||
| 809 | @@ -2291,7 +2291,6 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) | ||
| 810 | List *newcmds = NIL; | ||
| 811 | bool skipValidation = true; | ||
| 812 | AlterTableCmd *newcmd; | ||
| 813 | - LOCKMODE lockmode; | ||
| 814 | |||
| 815 | /* | ||
| 816 | * We must not scribble on the passed-in AlterTableStmt, so copy it. (This | ||
| 817 | @@ -2299,29 +2298,8 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) | ||
| 818 | */ | ||
| 819 | stmt = (AlterTableStmt *) copyObject(stmt); | ||
| 820 | |||
| 821 | - /* | ||
| 822 | - * Determine the appropriate lock level for this list of subcommands. | ||
| 823 | - */ | ||
| 824 | - lockmode = AlterTableGetLockLevel(stmt->cmds); | ||
| 825 | - | ||
| 826 | - /* | ||
| 827 | - * Acquire appropriate lock on the target relation, which will be held | ||
| 828 | - * until end of transaction. This ensures any decisions we make here | ||
| 829 | - * based on the state of the relation will still be good at execution. We | ||
| 830 | - * must get lock now because execution will later require it; taking a | ||
| 831 | - * lower grade lock now and trying to upgrade later risks deadlock. Any | ||
| 832 | - * new commands we add after this must not upgrade the lock level | ||
| 833 | - * requested here. | ||
| 834 | - */ | ||
| 835 | - rel = relation_openrv_extended(stmt->relation, lockmode, stmt->missing_ok); | ||
| 836 | - if (rel == NULL) | ||
| 837 | - { | ||
| 838 | - /* this message is consistent with relation_openrv */ | ||
| 839 | - ereport(NOTICE, | ||
| 840 | - (errmsg("relation \"%s\" does not exist, skipping", | ||
| 841 | - stmt->relation->relname))); | ||
| 842 | - return NIL; | ||
| 843 | - } | ||
| 844 | + /* Caller is responsible for locking the relation */ | ||
| 845 | + rel = relation_open(relid, NoLock); | ||
| 846 | |||
| 847 | /* Set up pstate and CreateStmtContext */ | ||
| 848 | pstate = make_parsestate(NULL); | ||
| 849 | @@ -2434,7 +2412,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) | ||
| 850 | IndexStmt *idxstmt = (IndexStmt *) lfirst(l); | ||
| 851 | |||
| 852 | Assert(IsA(idxstmt, IndexStmt)); | ||
| 853 | - idxstmt = transformIndexStmt(idxstmt, queryString); | ||
| 854 | + idxstmt = transformIndexStmt(relid, idxstmt, queryString); | ||
| 855 | newcmd = makeNode(AlterTableCmd); | ||
| 856 | newcmd->subtype = OidIsValid(idxstmt->indexOid) ? AT_AddIndexConstraint : AT_AddIndex; | ||
| 857 | newcmd->def = (Node *) idxstmt; | ||
| 858 | @@ -2458,7 +2436,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString) | ||
| 859 | newcmds = lappend(newcmds, newcmd); | ||
| 860 | } | ||
| 861 | |||
| 862 | - /* Close rel but keep lock */ | ||
| 863 | + /* Close rel */ | ||
| 864 | relation_close(rel, NoLock); | ||
| 865 | |||
| 866 | /* | ||
| 867 | diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c | ||
| 868 | index 509bf4d..7903e03 100644 | ||
| 869 | --- a/src/backend/tcop/utility.c | ||
| 870 | +++ b/src/backend/tcop/utility.c | ||
| 871 | @@ -67,49 +67,6 @@ ProcessUtility_hook_type ProcessUtility_hook = NULL; | ||
| 872 | |||
| 873 | |||
| 874 | /* | ||
| 875 | - * Verify user has ownership of specified relation, else ereport. | ||
| 876 | - * | ||
| 877 | - * If noCatalogs is true then we also deny access to system catalogs, | ||
| 878 | - * except when allowSystemTableMods is true. | ||
| 879 | - */ | ||
| 880 | -void | ||
| 881 | -CheckRelationOwnership(RangeVar *rel, bool noCatalogs) | ||
| 882 | -{ | ||
| 883 | - Oid relOid; | ||
| 884 | - HeapTuple tuple; | ||
| 885 | - | ||
| 886 | - /* | ||
| 887 | - * XXX: This is unsafe in the presence of concurrent DDL, since it is | ||
| 888 | - * called before acquiring any lock on the target relation. However, | ||
| 889 | - * locking the target relation (especially using something like | ||
| 890 | - * AccessExclusiveLock) before verifying that the user has permissions is | ||
| 891 | - * not appealing either. | ||
| 892 | - */ | ||
| 893 | - relOid = RangeVarGetRelid(rel, NoLock, false); | ||
| 894 | - | ||
| 895 | - tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relOid)); | ||
| 896 | - if (!HeapTupleIsValid(tuple)) /* should not happen */ | ||
| 897 | - elog(ERROR, "cache lookup failed for relation %u", relOid); | ||
| 898 | - | ||
| 899 | - if (!pg_class_ownercheck(relOid, GetUserId())) | ||
| 900 | - aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS, | ||
| 901 | - rel->relname); | ||
| 902 | - | ||
| 903 | - if (noCatalogs) | ||
| 904 | - { | ||
| 905 | - if (!allowSystemTableMods && | ||
| 906 | - IsSystemClass((Form_pg_class) GETSTRUCT(tuple))) | ||
| 907 | - ereport(ERROR, | ||
| 908 | - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), | ||
| 909 | - errmsg("permission denied: \"%s\" is a system catalog", | ||
| 910 | - rel->relname))); | ||
| 911 | - } | ||
| 912 | - | ||
| 913 | - ReleaseSysCache(tuple); | ||
| 914 | -} | ||
| 915 | - | ||
| 916 | - | ||
| 917 | -/* | ||
| 918 | * CommandIsReadOnly: is an executable query read-only? | ||
| 919 | * | ||
| 920 | * This is a much stricter test than we apply for XactReadOnly mode; | ||
| 921 | @@ -723,7 +680,8 @@ standard_ProcessUtility(Node *parsetree, | ||
| 922 | if (OidIsValid(relid)) | ||
| 923 | { | ||
| 924 | /* Run parse analysis ... */ | ||
| 925 | - stmts = transformAlterTableStmt(atstmt, queryString); | ||
| 926 | + stmts = transformAlterTableStmt(relid, atstmt, | ||
| 927 | + queryString); | ||
| 928 | |||
| 929 | /* ... and do it */ | ||
| 930 | foreach(l, stmts) | ||
| 931 | @@ -910,18 +868,36 @@ standard_ProcessUtility(Node *parsetree, | ||
| 932 | case T_IndexStmt: /* CREATE INDEX */ | ||
| 933 | { | ||
| 934 | IndexStmt *stmt = (IndexStmt *) parsetree; | ||
| 935 | + Oid relid; | ||
| 936 | + LOCKMODE lockmode; | ||
| 937 | |||
| 938 | if (stmt->concurrent) | ||
| 939 | PreventTransactionChain(isTopLevel, | ||
| 940 | "CREATE INDEX CONCURRENTLY"); | ||
| 941 | |||
| 942 | - CheckRelationOwnership(stmt->relation, true); | ||
| 943 | + /* | ||
| 944 | + * Look up the relation OID just once, right here at the | ||
| 945 | + * beginning, so that we don't end up repeating the name | ||
| 946 | + * lookup later and latching onto a different relation | ||
| 947 | + * partway through. To avoid lock upgrade hazards, it's | ||
| 948 | + * important that we take the strongest lock that will | ||
| 949 | + * eventually be needed here, so the lockmode calculation | ||
| 950 | + * needs to match what DefineIndex() does. | ||
| 951 | + */ | ||
| 952 | + lockmode = stmt->concurrent ? ShareUpdateExclusiveLock | ||
| 953 | + : ShareLock; | ||
| 954 | + relid = | ||
| 955 | + RangeVarGetRelidExtended(stmt->relation, lockmode, | ||
| 956 | + false, false, | ||
| 957 | + RangeVarCallbackOwnsRelation, | ||
| 958 | + NULL); | ||
| 959 | |||
| 960 | /* Run parse analysis ... */ | ||
| 961 | - stmt = transformIndexStmt(stmt, queryString); | ||
| 962 | + stmt = transformIndexStmt(relid, stmt, queryString); | ||
| 963 | |||
| 964 | /* ... and do it */ | ||
| 965 | - DefineIndex(stmt, | ||
| 966 | + DefineIndex(relid, /* OID of heap relation */ | ||
| 967 | + stmt, | ||
| 968 | InvalidOid, /* no predefined OID */ | ||
| 969 | false, /* is_alter_table */ | ||
| 970 | true, /* check_rights */ | ||
| 971 | @@ -1057,7 +1033,8 @@ standard_ProcessUtility(Node *parsetree, | ||
| 972 | |||
| 973 | case T_CreateTrigStmt: | ||
| 974 | (void) CreateTrigger((CreateTrigStmt *) parsetree, queryString, | ||
| 975 | - InvalidOid, InvalidOid, false); | ||
| 976 | + InvalidOid, InvalidOid, InvalidOid, | ||
| 977 | + InvalidOid, false); | ||
| 978 | break; | ||
| 979 | |||
| 980 | case T_CreatePLangStmt: | ||
| 981 | diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h | ||
| 982 | index d9d40b2..d8f8da4 100644 | ||
| 983 | --- a/src/include/catalog/pg_constraint.h | ||
| 984 | +++ b/src/include/catalog/pg_constraint.h | ||
| 985 | @@ -246,6 +246,7 @@ extern char *ChooseConstraintName(const char *name1, const char *name2, | ||
| 986 | |||
| 987 | extern void AlterConstraintNamespaces(Oid ownerId, Oid oldNspId, | ||
| 988 | Oid newNspId, bool isType, ObjectAddresses *objsMoved); | ||
| 989 | +extern void get_constraint_relation_oids(Oid constraint_oid, Oid *conrelid, Oid *confrelid); | ||
| 990 | extern Oid get_relation_constraint_oid(Oid relid, const char *conname, bool missing_ok); | ||
| 991 | extern Oid get_domain_constraint_oid(Oid typid, const char *conname, bool missing_ok); | ||
| 992 | |||
| 993 | diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h | ||
| 994 | index 9b6d57a..a00fd37 100644 | ||
| 995 | --- a/src/include/commands/defrem.h | ||
| 996 | +++ b/src/include/commands/defrem.h | ||
| 997 | @@ -20,7 +20,8 @@ | ||
| 998 | extern void RemoveObjects(DropStmt *stmt); | ||
| 999 | |||
| 1000 | /* commands/indexcmds.c */ | ||
| 1001 | -extern Oid DefineIndex(IndexStmt *stmt, | ||
| 1002 | +extern Oid DefineIndex(Oid relationId, | ||
| 1003 | + IndexStmt *stmt, | ||
| 1004 | Oid indexRelationId, | ||
| 1005 | bool is_alter_table, | ||
| 1006 | bool check_rights, | ||
| 1007 | @@ -35,7 +36,6 @@ extern char *makeObjectName(const char *name1, const char *name2, | ||
| 1008 | extern char *ChooseRelationName(const char *name1, const char *name2, | ||
| 1009 | const char *label, Oid namespaceid); | ||
| 1010 | extern bool CheckIndexCompatible(Oid oldId, | ||
| 1011 | - RangeVar *heapRelation, | ||
| 1012 | char *accessMethodName, | ||
| 1013 | List *attributeList, | ||
| 1014 | List *exclusionOpNames); | ||
| 1015 | diff --git a/src/include/commands/tablecmds.h b/src/include/commands/tablecmds.h | ||
| 1016 | index 4f32062..d41f8a1 100644 | ||
| 1017 | --- a/src/include/commands/tablecmds.h | ||
| 1018 | +++ b/src/include/commands/tablecmds.h | ||
| 1019 | @@ -78,4 +78,6 @@ extern void AtEOSubXact_on_commit_actions(bool isCommit, | ||
| 1020 | extern void RangeVarCallbackOwnsTable(const RangeVar *relation, | ||
| 1021 | Oid relId, Oid oldRelId, void *arg); | ||
| 1022 | |||
| 1023 | +extern void RangeVarCallbackOwnsRelation(const RangeVar *relation, | ||
| 1024 | + Oid relId, Oid oldRelId, void *noCatalogs); | ||
| 1025 | #endif /* TABLECMDS_H */ | ||
| 1026 | diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h | ||
| 1027 | index 9303341..0869c0b 100644 | ||
| 1028 | --- a/src/include/commands/trigger.h | ||
| 1029 | +++ b/src/include/commands/trigger.h | ||
| 1030 | @@ -109,7 +109,7 @@ extern PGDLLIMPORT int SessionReplicationRole; | ||
| 1031 | #define TRIGGER_DISABLED 'D' | ||
| 1032 | |||
| 1033 | extern Oid CreateTrigger(CreateTrigStmt *stmt, const char *queryString, | ||
| 1034 | - Oid constraintOid, Oid indexOid, | ||
| 1035 | + Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, | ||
| 1036 | bool isInternal); | ||
| 1037 | |||
| 1038 | extern void RemoveTriggerById(Oid trigOid); | ||
| 1039 | diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h | ||
| 1040 | index 327f7cf..31f5479 100644 | ||
| 1041 | --- a/src/include/nodes/parsenodes.h | ||
| 1042 | +++ b/src/include/nodes/parsenodes.h | ||
| 1043 | @@ -1566,6 +1566,8 @@ typedef struct Constraint | ||
| 1044 | /* Fields used for constraints that allow a NOT VALID specification */ | ||
| 1045 | bool skip_validation; /* skip validation of existing rows? */ | ||
| 1046 | bool initially_valid; /* mark the new constraint as valid? */ | ||
| 1047 | + | ||
| 1048 | + Oid old_pktable_oid; /* pg_constraint.confrelid of my former self */ | ||
| 1049 | } Constraint; | ||
| 1050 | |||
| 1051 | /* ---------------------- | ||
| 1052 | diff --git a/src/include/parser/parse_utilcmd.h b/src/include/parser/parse_utilcmd.h | ||
| 1053 | index 4ad793a..d8b340e 100644 | ||
| 1054 | --- a/src/include/parser/parse_utilcmd.h | ||
| 1055 | +++ b/src/include/parser/parse_utilcmd.h | ||
| 1056 | @@ -18,9 +18,10 @@ | ||
| 1057 | |||
| 1058 | |||
| 1059 | extern List *transformCreateStmt(CreateStmt *stmt, const char *queryString); | ||
| 1060 | -extern List *transformAlterTableStmt(AlterTableStmt *stmt, | ||
| 1061 | +extern List *transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, | ||
| 1062 | const char *queryString); | ||
| 1063 | -extern IndexStmt *transformIndexStmt(IndexStmt *stmt, const char *queryString); | ||
| 1064 | +extern IndexStmt *transformIndexStmt(Oid relid, IndexStmt *stmt, | ||
| 1065 | + const char *queryString); | ||
| 1066 | extern void transformRuleStmt(RuleStmt *stmt, const char *queryString, | ||
| 1067 | List **actions, Node **whereClause); | ||
| 1068 | extern List *transformCreateSchemaStmt(CreateSchemaStmt *stmt); | ||
| 1069 | diff --git a/src/include/tcop/utility.h b/src/include/tcop/utility.h | ||
| 1070 | index 54190b2..ae871ca 100644 | ||
| 1071 | --- a/src/include/tcop/utility.h | ||
| 1072 | +++ b/src/include/tcop/utility.h | ||
| 1073 | @@ -42,6 +42,4 @@ extern LogStmtLevel GetCommandLogLevel(Node *parsetree); | ||
| 1074 | |||
| 1075 | extern bool CommandIsReadOnly(Node *parsetree); | ||
| 1076 | |||
| 1077 | -extern void CheckRelationOwnership(RangeVar *rel, bool noCatalogs); | ||
| 1078 | - | ||
| 1079 | #endif /* UTILITY_H */ | ||
| 1080 | -- | ||
| 1081 | 1.7.5.4 | ||
| 1082 | |||
