@@ -295,12 +295,18 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
295
295
{'\0', 0, NULL, NULL, NULL, NULL}
296
296
};
297
297
298
+ /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
298
299
struct DropRelationCallbackState
299
300
{
300
- char relkind;
301
+ /* These fields are set by RemoveRelations: */
302
+ char expected_relkind;
303
+ LOCKMODE heap_lockmode;
304
+ /* These fields are state to track which subsidiary locks are held: */
301
305
Oid heapOid;
302
306
Oid partParentOid;
303
- bool concurrent;
307
+ /* These fields are passed back by RangeVarCallbackForDropRelation: */
308
+ char actual_relkind;
309
+ char actual_relpersistence;
304
310
};
305
311
306
312
/* Alter table target-type flags for ATSimplePermissions */
@@ -1416,10 +1422,13 @@ RemoveRelations(DropStmt *drop)
1416
1422
AcceptInvalidationMessages();
1417
1423
1418
1424
/* Look up the appropriate relation using namespace search. */
1419
- state.relkind = relkind;
1425
+ state.expected_relkind = relkind;
1426
+ state.heap_lockmode = drop->concurrent ?
1427
+ ShareUpdateExclusiveLock : AccessExclusiveLock;
1428
+ /* We must initialize these fields to show that no locks are held: */
1420
1429
state.heapOid = InvalidOid;
1421
1430
state.partParentOid = InvalidOid;
1422
- state.concurrent = drop->concurrent;
1431
+
1423
1432
relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1424
1433
RangeVarCallbackForDropRelation,
1425
1434
(void *) &state);
@@ -1433,10 +1442,10 @@ RemoveRelations(DropStmt *drop)
1433
1442
1434
1443
/*
1435
1444
* Decide if concurrent mode needs to be used here or not. The
1436
- * relation persistence cannot be known without its OID .
1445
+ * callback retrieved the rel's persistence for us .
1437
1446
*/
1438
1447
if (drop->concurrent &&
1439
- get_rel_persistence(relOid) != RELPERSISTENCE_TEMP)
1448
+ state.actual_relpersistence != RELPERSISTENCE_TEMP)
1440
1449
{
1441
1450
Assert(list_length(drop->objects) == 1 &&
1442
1451
drop->removeType == OBJECT_INDEX);
@@ -1448,12 +1457,24 @@ RemoveRelations(DropStmt *drop)
1448
1457
* either.
1449
1458
*/
1450
1459
if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1451
- get_rel_relkind(relOid) == RELKIND_PARTITIONED_INDEX)
1460
+ state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1452
1461
ereport(ERROR,
1453
1462
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1454
1463
errmsg("cannot drop partitioned index \"%s\" concurrently",
1455
1464
rel->relname)));
1456
1465
1466
+ /*
1467
+ * If we're told to drop a partitioned index, we must acquire lock on
1468
+ * all the children of its parent partitioned table before proceeding.
1469
+ * Otherwise we'd try to lock the child index partitions before their
1470
+ * tables, leading to potential deadlock against other sessions that
1471
+ * will lock those objects in the other order.
1472
+ */
1473
+ if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1474
+ (void) find_all_inheritors(state.heapOid,
1475
+ state.heap_lockmode,
1476
+ NULL);
1477
+
1457
1478
/* OK, we're ready to delete this one */
1458
1479
obj.classId = RelationRelationId;
1459
1480
obj.objectId = relOid;
@@ -1479,17 +1500,14 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1479
1500
{
1480
1501
HeapTuple tuple;
1481
1502
struct DropRelationCallbackState *state;
1482
- char relkind;
1483
1503
char expected_relkind;
1484
1504
bool is_partition;
1485
1505
Form_pg_class classform;
1486
1506
LOCKMODE heap_lockmode;
1487
1507
bool invalid_system_index = false;
1488
1508
1489
1509
state = (struct DropRelationCallbackState *) arg;
1490
- relkind = state->relkind;
1491
- heap_lockmode = state->concurrent ?
1492
- ShareUpdateExclusiveLock : AccessExclusiveLock;
1510
+ heap_lockmode = state->heap_lockmode;
1493
1511
1494
1512
/*
1495
1513
* If we previously locked some other index's heap, and the name we're
@@ -1523,6 +1541,10 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1523
1541
classform = (Form_pg_class) GETSTRUCT(tuple);
1524
1542
is_partition = classform->relispartition;
1525
1543
1544
+ /* Pass back some data to save lookups in RemoveRelations */
1545
+ state->actual_relkind = classform->relkind;
1546
+ state->actual_relpersistence = classform->relpersistence;
1547
+
1526
1548
/*
1527
1549
* Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1528
1550
* but RemoveRelations() can only pass one relkind for a given relation.
@@ -1538,13 +1560,15 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1538
1560
else
1539
1561
expected_relkind = classform->relkind;
1540
1562
1541
- if (relkind != expected_relkind)
1542
- DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
1563
+ if (state->expected_relkind != expected_relkind)
1564
+ DropErrorMsgWrongType(rel->relname, classform->relkind,
1565
+ state->expected_relkind);
1543
1566
1544
1567
/* Allow DROP to either table owner or schema owner */
1545
1568
if (!pg_class_ownercheck(relOid, GetUserId()) &&
1546
1569
!pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
1547
- aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
1570
+ aclcheck_error(ACLCHECK_NOT_OWNER,
1571
+ get_relkind_objtype(classform->relkind),
1548
1572
rel->relname);
1549
1573
1550
1574
/*
@@ -1553,7 +1577,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1553
1577
* only concerns indexes of toast relations that became invalid during a
1554
1578
* REINDEX CONCURRENTLY process.
1555
1579
*/
1556
- if (IsSystemClass(relOid, classform) && relkind == RELKIND_INDEX)
1580
+ if (IsSystemClass(relOid, classform) && classform-> relkind == RELKIND_INDEX)
1557
1581
{
1558
1582
HeapTuple locTuple;
1559
1583
Form_pg_index indexform;
@@ -1589,9 +1613,10 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1589
1613
* locking the index. index_drop() will need this anyway, and since
1590
1614
* regular queries lock tables before their indexes, we risk deadlock if
1591
1615
* we do it the other way around. No error if we don't find a pg_index
1592
- * entry, though --- the relation may have been dropped.
1616
+ * entry, though --- the relation may have been dropped. Note that this
1617
+ * code will execute for either plain or partitioned indexes.
1593
1618
*/
1594
- if ((relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX) &&
1619
+ if (expected_relkind == RELKIND_INDEX &&
1595
1620
relOid != oldRelOid)
1596
1621
{
1597
1622
state->heapOid = IndexGetRelation(relOid, true);
@@ -1602,7 +1627,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1602
1627
/*
1603
1628
* Similarly, if the relation is a partition, we must acquire lock on its
1604
1629
* parent before locking the partition. That's because queries lock the
1605
- * parent before its partitions, so we risk deadlock it we do it the other
1630
+ * parent before its partitions, so we risk deadlock if we do it the other
1606
1631
* way around.
1607
1632
*/
1608
1633
if (is_partition && relOid != oldRelOid)
0 commit comments