Skip to content

Commit 725d981

Browse files
committed
Ensure that pg_amop/amproc entries depend on their lefttype/righttype.
Usually an entry in pg_amop or pg_amproc does not need a dependency on its amoplefttype/amoprighttype/amproclefttype/amprocrighttype types, because there is an indirect dependency via the argument types of its referenced operator or procedure, or via the opclass it belongs to. However, for some support procedures in some index AMs, the argument types of the support procedure might not mention the column data type at all. Also, the amop/amproc entry might be treated as "loose" in the opfamily, in which case it lacks a dependency on any particular opclass; or it might be a cross-type entry having a reference to a datatype that is not its opclass' opcintype. The upshot of all this is that there are cases where a datatype can be dropped while leaving behind amop/amproc entries that mention it, because there is no path in pg_depend showing that those entries depend on that type. Such entries are harmless in normal activity, because they won't get used, but they cause problems for maintenance actions such as dropping the operator family. They also cause pg_dump to produce bogus output. The previous commit put a band-aid on the DROP OPERATOR FAMILY failure, but a real fix is needed. To fix, add pg_depend entries showing that a pg_amop/pg_amproc entry depends on its lefttype/righttype. To avoid bloating pg_depend too much, skip this if the referenced operator or function has that type as an input type. (I did not bother with considering the possible indirect dependency via the opclass' opcintype; at least in the reported case, that wouldn't help anyway.) Probably, the reason this has escaped notice for so long is that add-on datatypes and relevant opclasses/opfamilies are usually packaged as extensions nowadays, so that there's no way to drop a type without dropping the referencing opclasses/opfamilies too. Still, in the absence of pg_depend entries there's nothing that constrains DROP EXTENSION to drop the opfamily entries before the datatype, so it seems possible for a DROP failure to occur anyway. The specific case that was reported doesn't fail in v13, because v13 prefers to attach the support procedure to the opclass not the opfamily. But it's surely possible to construct other edge cases that do fail in v13, so patch that too. Per report from Yoran Heling. Back-patch to all supported branches. Discussion: https://postgr.es/m/Z1MVCOh1hprjK5Sf@gmai021
1 parent 531cbd8 commit 725d981

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed

src/backend/commands/opclasscmds.c

+91
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ static void storeOperators(List *opfamilyname, Oid amoid,
6969
static void storeProcedures(List *opfamilyname, Oid amoid,
7070
Oid opfamilyoid, Oid opclassoid,
7171
List *procedures, bool isAdd);
72+
static bool typeDepNeeded(Oid typid, OpFamilyMember *member, bool isProc);
7273
static void dropOperators(List *opfamilyname, Oid amoid, Oid opfamilyoid,
7374
List *operators);
7475
static void dropProcedures(List *opfamilyname, Oid amoid, Oid opfamilyoid,
@@ -1390,6 +1391,7 @@ storeOperators(List *opfamilyname, Oid amoid,
13901391
foreach(l, operators)
13911392
{
13921393
OpFamilyMember *op = (OpFamilyMember *) lfirst(l);
1394+
DependencyType opdeptype;
13931395
char oppurpose;
13941396

13951397
/*
@@ -1446,6 +1448,7 @@ storeOperators(List *opfamilyname, Oid amoid,
14461448
if (OidIsValid(opclassoid))
14471449
{
14481450
/* if contained in an opclass, use a NORMAL dep on operator */
1451+
opdeptype = DEPENDENCY_NORMAL;
14491452
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
14501453

14511454
/* ... and an INTERNAL dep on the opclass */
@@ -1457,6 +1460,7 @@ storeOperators(List *opfamilyname, Oid amoid,
14571460
else
14581461
{
14591462
/* if "loose" in the opfamily, use a AUTO dep on operator */
1463+
opdeptype = DEPENDENCY_AUTO;
14601464
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
14611465

14621466
/* ... and an AUTO dep on the opfamily */
@@ -1466,6 +1470,27 @@ storeOperators(List *opfamilyname, Oid amoid,
14661470
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
14671471
}
14681472

1473+
if (typeDepNeeded(op->lefttype, op, false))
1474+
{
1475+
referenced.classId = TypeRelationId;
1476+
referenced.objectId = op->lefttype;
1477+
referenced.objectSubId = 0;
1478+
1479+
/* use same dependency type as for operator */
1480+
recordDependencyOn(&myself, &referenced, opdeptype);
1481+
}
1482+
1483+
if (op->lefttype != op->righttype &&
1484+
typeDepNeeded(op->righttype, op, false))
1485+
{
1486+
referenced.classId = TypeRelationId;
1487+
referenced.objectId = op->righttype;
1488+
referenced.objectSubId = 0;
1489+
1490+
/* use same dependency type as for operator */
1491+
recordDependencyOn(&myself, &referenced, opdeptype);
1492+
}
1493+
14691494
/* A search operator also needs a dep on the referenced opfamily */
14701495
if (OidIsValid(op->sortfamily))
14711496
{
@@ -1508,6 +1533,7 @@ storeProcedures(List *opfamilyname, Oid amoid,
15081533
foreach(l, procedures)
15091534
{
15101535
OpFamilyMember *proc = (OpFamilyMember *) lfirst(l);
1536+
DependencyType procdeptype;
15111537

15121538
/*
15131539
* If adding to an existing family, check for conflict with an
@@ -1558,6 +1584,7 @@ storeProcedures(List *opfamilyname, Oid amoid,
15581584
if (OidIsValid(opclassoid))
15591585
{
15601586
/* if contained in an opclass, use a NORMAL dep on procedure */
1587+
procdeptype = DEPENDENCY_NORMAL;
15611588
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
15621589

15631590
/* ... and an INTERNAL dep on the opclass */
@@ -1569,6 +1596,7 @@ storeProcedures(List *opfamilyname, Oid amoid,
15691596
else
15701597
{
15711598
/* if "loose" in the opfamily, use a AUTO dep on procedure */
1599+
procdeptype = DEPENDENCY_AUTO;
15721600
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
15731601

15741602
/* ... and an AUTO dep on the opfamily */
@@ -1577,6 +1605,28 @@ storeProcedures(List *opfamilyname, Oid amoid,
15771605
referenced.objectSubId = 0;
15781606
recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO);
15791607
}
1608+
1609+
if (typeDepNeeded(proc->lefttype, proc, true))
1610+
{
1611+
referenced.classId = TypeRelationId;
1612+
referenced.objectId = proc->lefttype;
1613+
referenced.objectSubId = 0;
1614+
1615+
/* use same dependency type as for procedure */
1616+
recordDependencyOn(&myself, &referenced, procdeptype);
1617+
}
1618+
1619+
if (proc->lefttype != proc->righttype &&
1620+
typeDepNeeded(proc->righttype, proc, true))
1621+
{
1622+
referenced.classId = TypeRelationId;
1623+
referenced.objectId = proc->righttype;
1624+
referenced.objectSubId = 0;
1625+
1626+
/* use same dependency type as for procedure */
1627+
recordDependencyOn(&myself, &referenced, procdeptype);
1628+
}
1629+
15801630
/* Post create hook of access method procedure */
15811631
InvokeObjectPostCreateHook(AccessMethodProcedureRelationId,
15821632
entryoid, 0);
@@ -1585,6 +1635,47 @@ storeProcedures(List *opfamilyname, Oid amoid,
15851635
table_close(rel, RowExclusiveLock);
15861636
}
15871637

1638+
/*
1639+
* Detect whether a pg_amop or pg_amproc entry needs an explicit dependency
1640+
* on its lefttype or righttype.
1641+
*
1642+
* We make such a dependency unless the entry has an indirect dependency
1643+
* via its referenced operator or function. That's nearly always true
1644+
* for operators, but might well not be true for support functions.
1645+
*/
1646+
static bool
1647+
typeDepNeeded(Oid typid, OpFamilyMember *member, bool isProc)
1648+
{
1649+
bool result = true;
1650+
1651+
if (isProc)
1652+
{
1653+
Oid *argtypes;
1654+
int nargs;
1655+
1656+
(void) get_func_signature(member->object, &argtypes, &nargs);
1657+
for (int i = 0; i < nargs; i++)
1658+
{
1659+
if (typid == argtypes[i])
1660+
{
1661+
result = false; /* match, no dependency needed */
1662+
break;
1663+
}
1664+
}
1665+
pfree(argtypes);
1666+
}
1667+
else
1668+
{
1669+
Oid lefttype,
1670+
righttype;
1671+
1672+
op_input_types(member->object, &lefttype, &righttype);
1673+
if (typid == lefttype || typid == righttype)
1674+
result = false; /* match, no dependency needed */
1675+
}
1676+
return result;
1677+
}
1678+
15881679

15891680
/*
15901681
* Remove operator entries from an opfamily.

0 commit comments

Comments
 (0)