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