@@ -278,12 +278,18 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
278
278
{'\0', 0, NULL, NULL, NULL, NULL}
279
279
};
280
280
281
+ /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
281
282
struct DropRelationCallbackState
282
283
{
283
- char relkind;
284
+ /* These fields are set by RemoveRelations: */
285
+ char expected_relkind;
286
+ LOCKMODE heap_lockmode;
287
+ /* These fields are state to track which subsidiary locks are held: */
284
288
Oid heapOid;
285
289
Oid partParentOid;
286
- bool concurrent;
290
+ /* These fields are passed back by RangeVarCallbackForDropRelation: */
291
+ char actual_relkind;
292
+ char actual_relpersistence;
287
293
};
288
294
289
295
/* Alter table target-type flags for ATSimplePermissions */
@@ -1323,10 +1329,13 @@ RemoveRelations(DropStmt *drop)
1323
1329
AcceptInvalidationMessages();
1324
1330
1325
1331
/* Look up the appropriate relation using namespace search. */
1326
- state.relkind = relkind;
1332
+ state.expected_relkind = relkind;
1333
+ state.heap_lockmode = drop->concurrent ?
1334
+ ShareUpdateExclusiveLock : AccessExclusiveLock;
1335
+ /* We must initialize these fields to show that no locks are held: */
1327
1336
state.heapOid = InvalidOid;
1328
1337
state.partParentOid = InvalidOid;
1329
- state.concurrent = drop->concurrent;
1338
+
1330
1339
relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1331
1340
RangeVarCallbackForDropRelation,
1332
1341
(void *) &state);
@@ -1340,10 +1349,10 @@ RemoveRelations(DropStmt *drop)
1340
1349
1341
1350
/*
1342
1351
* Decide if concurrent mode needs to be used here or not. The
1343
- * relation persistence cannot be known without its OID .
1352
+ * callback retrieved the rel's persistence for us .
1344
1353
*/
1345
1354
if (drop->concurrent &&
1346
- get_rel_persistence(relOid) != RELPERSISTENCE_TEMP)
1355
+ state.actual_relpersistence != RELPERSISTENCE_TEMP)
1347
1356
{
1348
1357
Assert(list_length(drop->objects) == 1 &&
1349
1358
drop->removeType == OBJECT_INDEX);
@@ -1355,12 +1364,24 @@ RemoveRelations(DropStmt *drop)
1355
1364
* either.
1356
1365
*/
1357
1366
if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1358
- get_rel_relkind(relOid) == RELKIND_PARTITIONED_INDEX)
1367
+ state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1359
1368
ereport(ERROR,
1360
1369
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1361
1370
errmsg("cannot drop partitioned index \"%s\" concurrently",
1362
1371
rel->relname)));
1363
1372
1373
+ /*
1374
+ * If we're told to drop a partitioned index, we must acquire lock on
1375
+ * all the children of its parent partitioned table before proceeding.
1376
+ * Otherwise we'd try to lock the child index partitions before their
1377
+ * tables, leading to potential deadlock against other sessions that
1378
+ * will lock those objects in the other order.
1379
+ */
1380
+ if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1381
+ (void) find_all_inheritors(state.heapOid,
1382
+ state.heap_lockmode,
1383
+ NULL);
1384
+
1364
1385
/* OK, we're ready to delete this one */
1365
1386
obj.classId = RelationRelationId;
1366
1387
obj.objectId = relOid;
@@ -1386,17 +1407,14 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1386
1407
{
1387
1408
HeapTuple tuple;
1388
1409
struct DropRelationCallbackState *state;
1389
- char relkind;
1390
1410
char expected_relkind;
1391
1411
bool is_partition;
1392
1412
Form_pg_class classform;
1393
1413
LOCKMODE heap_lockmode;
1394
1414
bool invalid_system_index = false;
1395
1415
1396
1416
state = (struct DropRelationCallbackState *) arg;
1397
- relkind = state->relkind;
1398
- heap_lockmode = state->concurrent ?
1399
- ShareUpdateExclusiveLock : AccessExclusiveLock;
1417
+ heap_lockmode = state->heap_lockmode;
1400
1418
1401
1419
/*
1402
1420
* If we previously locked some other index's heap, and the name we're
@@ -1430,6 +1448,10 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1430
1448
classform = (Form_pg_class) GETSTRUCT(tuple);
1431
1449
is_partition = classform->relispartition;
1432
1450
1451
+ /* Pass back some data to save lookups in RemoveRelations */
1452
+ state->actual_relkind = classform->relkind;
1453
+ state->actual_relpersistence = classform->relpersistence;
1454
+
1433
1455
/*
1434
1456
* Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1435
1457
* but RemoveRelations() can only pass one relkind for a given relation.
@@ -1445,13 +1467,15 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1445
1467
else
1446
1468
expected_relkind = classform->relkind;
1447
1469
1448
- if (relkind != expected_relkind)
1449
- DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
1470
+ if (state->expected_relkind != expected_relkind)
1471
+ DropErrorMsgWrongType(rel->relname, classform->relkind,
1472
+ state->expected_relkind);
1450
1473
1451
1474
/* Allow DROP to either table owner or schema owner */
1452
1475
if (!pg_class_ownercheck(relOid, GetUserId()) &&
1453
1476
!pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
1454
- aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
1477
+ aclcheck_error(ACLCHECK_NOT_OWNER,
1478
+ get_relkind_objtype(classform->relkind),
1455
1479
rel->relname);
1456
1480
1457
1481
/*
@@ -1460,7 +1484,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1460
1484
* only concerns indexes of toast relations that became invalid during a
1461
1485
* REINDEX CONCURRENTLY process.
1462
1486
*/
1463
- if (IsSystemClass(relOid, classform) && relkind == RELKIND_INDEX)
1487
+ if (IsSystemClass(relOid, classform) && classform-> relkind == RELKIND_INDEX)
1464
1488
{
1465
1489
HeapTuple locTuple;
1466
1490
Form_pg_index indexform;
@@ -1496,9 +1520,10 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1496
1520
* locking the index. index_drop() will need this anyway, and since
1497
1521
* regular queries lock tables before their indexes, we risk deadlock if
1498
1522
* we do it the other way around. No error if we don't find a pg_index
1499
- * entry, though --- the relation may have been dropped.
1523
+ * entry, though --- the relation may have been dropped. Note that this
1524
+ * code will execute for either plain or partitioned indexes.
1500
1525
*/
1501
- if ((relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX) &&
1526
+ if (expected_relkind == RELKIND_INDEX &&
1502
1527
relOid != oldRelOid)
1503
1528
{
1504
1529
state->heapOid = IndexGetRelation(relOid, true);
@@ -1509,7 +1534,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1509
1534
/*
1510
1535
* Similarly, if the relation is a partition, we must acquire lock on its
1511
1536
* parent before locking the partition. That's because queries lock the
1512
- * parent before its partitions, so we risk deadlock it we do it the other
1537
+ * parent before its partitions, so we risk deadlock if we do it the other
1513
1538
* way around.
1514
1539
*/
1515
1540
if (is_partition && relOid != oldRelOid)
0 commit comments