@@ -280,12 +280,18 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
280
280
{'\0', 0, NULL, NULL, NULL, NULL}
281
281
};
282
282
283
+ /* communication between RemoveRelations and RangeVarCallbackForDropRelation */
283
284
struct DropRelationCallbackState
284
285
{
285
- char relkind;
286
+ /* These fields are set by RemoveRelations: */
287
+ char expected_relkind;
288
+ LOCKMODE heap_lockmode;
289
+ /* These fields are state to track which subsidiary locks are held: */
286
290
Oid heapOid;
287
291
Oid partParentOid;
288
- bool concurrent;
292
+ /* These fields are passed back by RangeVarCallbackForDropRelation: */
293
+ char actual_relkind;
294
+ char actual_relpersistence;
289
295
};
290
296
291
297
/* Alter table target-type flags for ATSimplePermissions */
@@ -1349,10 +1355,13 @@ RemoveRelations(DropStmt *drop)
1349
1355
AcceptInvalidationMessages();
1350
1356
1351
1357
/* Look up the appropriate relation using namespace search. */
1352
- state.relkind = relkind;
1358
+ state.expected_relkind = relkind;
1359
+ state.heap_lockmode = drop->concurrent ?
1360
+ ShareUpdateExclusiveLock : AccessExclusiveLock;
1361
+ /* We must initialize these fields to show that no locks are held: */
1353
1362
state.heapOid = InvalidOid;
1354
1363
state.partParentOid = InvalidOid;
1355
- state.concurrent = drop->concurrent;
1364
+
1356
1365
relOid = RangeVarGetRelidExtended(rel, lockmode, RVR_MISSING_OK,
1357
1366
RangeVarCallbackForDropRelation,
1358
1367
(void *) &state);
@@ -1366,10 +1375,10 @@ RemoveRelations(DropStmt *drop)
1366
1375
1367
1376
/*
1368
1377
* Decide if concurrent mode needs to be used here or not. The
1369
- * relation persistence cannot be known without its OID .
1378
+ * callback retrieved the rel's persistence for us .
1370
1379
*/
1371
1380
if (drop->concurrent &&
1372
- get_rel_persistence(relOid) != RELPERSISTENCE_TEMP)
1381
+ state.actual_relpersistence != RELPERSISTENCE_TEMP)
1373
1382
{
1374
1383
Assert(list_length(drop->objects) == 1 &&
1375
1384
drop->removeType == OBJECT_INDEX);
@@ -1381,12 +1390,24 @@ RemoveRelations(DropStmt *drop)
1381
1390
* either.
1382
1391
*/
1383
1392
if ((flags & PERFORM_DELETION_CONCURRENTLY) != 0 &&
1384
- get_rel_relkind(relOid) == RELKIND_PARTITIONED_INDEX)
1393
+ state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1385
1394
ereport(ERROR,
1386
1395
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1387
1396
errmsg("cannot drop partitioned index \"%s\" concurrently",
1388
1397
rel->relname)));
1389
1398
1399
+ /*
1400
+ * If we're told to drop a partitioned index, we must acquire lock on
1401
+ * all the children of its parent partitioned table before proceeding.
1402
+ * Otherwise we'd try to lock the child index partitions before their
1403
+ * tables, leading to potential deadlock against other sessions that
1404
+ * will lock those objects in the other order.
1405
+ */
1406
+ if (state.actual_relkind == RELKIND_PARTITIONED_INDEX)
1407
+ (void) find_all_inheritors(state.heapOid,
1408
+ state.heap_lockmode,
1409
+ NULL);
1410
+
1390
1411
/* OK, we're ready to delete this one */
1391
1412
obj.classId = RelationRelationId;
1392
1413
obj.objectId = relOid;
@@ -1412,17 +1433,14 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1412
1433
{
1413
1434
HeapTuple tuple;
1414
1435
struct DropRelationCallbackState *state;
1415
- char relkind;
1416
1436
char expected_relkind;
1417
1437
bool is_partition;
1418
1438
Form_pg_class classform;
1419
1439
LOCKMODE heap_lockmode;
1420
1440
bool invalid_system_index = false;
1421
1441
1422
1442
state = (struct DropRelationCallbackState *) arg;
1423
- relkind = state->relkind;
1424
- heap_lockmode = state->concurrent ?
1425
- ShareUpdateExclusiveLock : AccessExclusiveLock;
1443
+ heap_lockmode = state->heap_lockmode;
1426
1444
1427
1445
/*
1428
1446
* If we previously locked some other index's heap, and the name we're
@@ -1456,6 +1474,10 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1456
1474
classform = (Form_pg_class) GETSTRUCT(tuple);
1457
1475
is_partition = classform->relispartition;
1458
1476
1477
+ /* Pass back some data to save lookups in RemoveRelations */
1478
+ state->actual_relkind = classform->relkind;
1479
+ state->actual_relpersistence = classform->relpersistence;
1480
+
1459
1481
/*
1460
1482
* Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
1461
1483
* but RemoveRelations() can only pass one relkind for a given relation.
@@ -1471,13 +1493,15 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1471
1493
else
1472
1494
expected_relkind = classform->relkind;
1473
1495
1474
- if (relkind != expected_relkind)
1475
- DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
1496
+ if (state->expected_relkind != expected_relkind)
1497
+ DropErrorMsgWrongType(rel->relname, classform->relkind,
1498
+ state->expected_relkind);
1476
1499
1477
1500
/* Allow DROP to either table owner or schema owner */
1478
1501
if (!pg_class_ownercheck(relOid, GetUserId()) &&
1479
1502
!pg_namespace_ownercheck(classform->relnamespace, GetUserId()))
1480
- aclcheck_error(ACLCHECK_NOT_OWNER, get_relkind_objtype(get_rel_relkind(relOid)),
1503
+ aclcheck_error(ACLCHECK_NOT_OWNER,
1504
+ get_relkind_objtype(classform->relkind),
1481
1505
rel->relname);
1482
1506
1483
1507
/*
@@ -1486,7 +1510,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1486
1510
* only concerns indexes of toast relations that became invalid during a
1487
1511
* REINDEX CONCURRENTLY process.
1488
1512
*/
1489
- if (IsSystemClass(relOid, classform) && relkind == RELKIND_INDEX)
1513
+ if (IsSystemClass(relOid, classform) && classform-> relkind == RELKIND_INDEX)
1490
1514
{
1491
1515
HeapTuple locTuple;
1492
1516
Form_pg_index indexform;
@@ -1522,9 +1546,10 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1522
1546
* locking the index. index_drop() will need this anyway, and since
1523
1547
* regular queries lock tables before their indexes, we risk deadlock if
1524
1548
* we do it the other way around. No error if we don't find a pg_index
1525
- * entry, though --- the relation may have been dropped.
1549
+ * entry, though --- the relation may have been dropped. Note that this
1550
+ * code will execute for either plain or partitioned indexes.
1526
1551
*/
1527
- if ((relkind == RELKIND_INDEX || relkind == RELKIND_PARTITIONED_INDEX) &&
1552
+ if (expected_relkind == RELKIND_INDEX &&
1528
1553
relOid != oldRelOid)
1529
1554
{
1530
1555
state->heapOid = IndexGetRelation(relOid, true);
@@ -1535,7 +1560,7 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
1535
1560
/*
1536
1561
* Similarly, if the relation is a partition, we must acquire lock on its
1537
1562
* parent before locking the partition. That's because queries lock the
1538
- * parent before its partitions, so we risk deadlock it we do it the other
1563
+ * parent before its partitions, so we risk deadlock if we do it the other
1539
1564
* way around.
1540
1565
*/
1541
1566
if (is_partition && relOid != oldRelOid)
0 commit comments