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, 1082 insertions, 0 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 new file mode 100644 index 000000000..f1aa21250 --- /dev/null +++ b/meta-oe/recipes-support/postgresql/files/0005-Avoid-repeated-name-lookups-during-table-and-index-D.patch | |||
@@ -0,0 +1,1082 @@ | |||
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 | |||