Skip to content

Commit 7b01246

Browse files
committed
Prevent ALTER TYPE/DOMAIN/OPERATOR from changing extension membership.
If recordDependencyOnCurrentExtension is invoked on a pre-existing, free-standing object during an extension update script, that object will become owned by the extension. In our current code this is possible in three cases: * Replacing a "shell" type or operator. * CREATE OR REPLACE overwriting an existing object. * ALTER TYPE SET, ALTER DOMAIN SET, and ALTER OPERATOR SET. The first of these cases is intentional behavior, as noted by the existing comments for GenerateTypeDependencies. It seems like appropriate behavior for CREATE OR REPLACE too; at least, the obvious alternatives are not better. However, the fact that it happens during ALTER is an artifact of trying to share code (GenerateTypeDependencies and makeOperatorDependencies) between the CREATE and ALTER cases. Since an extension script would be unlikely to ALTER an object that didn't already belong to the extension, this behavior is not very troubling for the direct target object ... but ALTER TYPE SET will recurse to dependent domains, and it is very uncool for those to become owned by the extension if they were not already. Let's fix this by redefining the ALTER cases to never change extension membership, full stop. We could minimize the behavioral change by only changing the behavior when ALTER TYPE SET is recursing to a domain, but that would complicate the code and it does not seem like a better definition. Per bug #17144 from Alex Kozhemyakin. Back-patch to v13 where ALTER TYPE SET was added. (The other cases are older, but since they only affect the directly-named object, there's not enough of a problem to justify changing the behavior further back.) Discussion: https://postgr.es/m/17144-e67d7a8f049de9af@postgresql.org
1 parent e15f32f commit 7b01246

File tree

7 files changed

+41
-14
lines changed

7 files changed

+41
-14
lines changed

src/backend/catalog/pg_depend.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ recordMultipleDependencies(const ObjectAddress *depender,
133133
* existed), so we must check for a pre-existing extension membership entry.
134134
* Passing false is a guarantee that the object is newly created, and so
135135
* could not already be a member of any extension.
136+
*
137+
* Note: isReplace = true is typically used when updating a object in
138+
* CREATE OR REPLACE and similar commands. The net effect is that if an
139+
* extension script uses such a command on a pre-existing free-standing
140+
* object, the object will be absorbed into the extension. If the object
141+
* is already a member of some other extension, the command will fail.
142+
* This behavior is desirable for cases such as replacing a shell type.
136143
*/
137144
void
138145
recordDependencyOnCurrentExtension(const ObjectAddress *object,

src/backend/catalog/pg_operator.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ OperatorShellMake(const char *operatorName,
268268
CatalogTupleInsert(pg_operator_desc, tup);
269269

270270
/* Add dependencies for the entry */
271-
makeOperatorDependencies(tup, false);
271+
makeOperatorDependencies(tup, true, false);
272272

273273
heap_freetuple(tup);
274274

@@ -546,7 +546,7 @@ OperatorCreate(const char *operatorName,
546546
}
547547

548548
/* Add dependencies for the entry */
549-
address = makeOperatorDependencies(tup, isUpdate);
549+
address = makeOperatorDependencies(tup, true, isUpdate);
550550

551551
/* Post creation hook for new operator */
552552
InvokeObjectPostCreateHook(OperatorRelationId, operatorObjectId, 0);
@@ -766,11 +766,17 @@ OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete)
766766
* complete operator, a new shell operator, a just-updated shell,
767767
* or an operator that's being modified by ALTER OPERATOR).
768768
*
769+
* makeExtensionDep should be true when making a new operator or
770+
* replacing a shell, false for ALTER OPERATOR. Passing false
771+
* will prevent any change in the operator's extension membership.
772+
*
769773
* NB: the OidIsValid tests in this routine are necessary, in case
770774
* the given operator is a shell.
771775
*/
772776
ObjectAddress
773-
makeOperatorDependencies(HeapTuple tuple, bool isUpdate)
777+
makeOperatorDependencies(HeapTuple tuple,
778+
bool makeExtensionDep,
779+
bool isUpdate)
774780
{
775781
Form_pg_operator oper = (Form_pg_operator) GETSTRUCT(tuple);
776782
ObjectAddress myself,
@@ -867,7 +873,8 @@ makeOperatorDependencies(HeapTuple tuple, bool isUpdate)
867873
oper->oprowner);
868874

869875
/* Dependency on extension */
870-
recordDependencyOnCurrentExtension(&myself, true);
876+
if (makeExtensionDep)
877+
recordDependencyOnCurrentExtension(&myself, true);
871878

872879
return myself;
873880
}

src/backend/catalog/pg_type.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
162162
0,
163163
false,
164164
false,
165+
true, /* make extension dependency */
165166
false);
166167

167168
/* Post creation hook for new shell type */
@@ -497,6 +498,7 @@ TypeCreate(Oid newTypeOid,
497498
relationKind,
498499
isImplicitArray,
499500
isDependentType,
501+
true, /* make extension dependency */
500502
rebuildDeps);
501503

502504
/* Post creation hook for new type */
@@ -530,13 +532,17 @@ TypeCreate(Oid newTypeOid,
530532
* isDependentType is true if this is an implicit array or relation rowtype;
531533
* that means it doesn't need its own dependencies on owner etc.
532534
*
533-
* If rebuild is true, we remove existing dependencies and rebuild them
534-
* from scratch. This is needed for ALTER TYPE, and also when replacing
535-
* a shell type. We don't remove an existing extension dependency, though.
536-
* (That means an extension can't absorb a shell type created in another
537-
* extension, nor ALTER a type created by another extension. Also, if it
538-
* replaces a free-standing shell type or ALTERs a free-standing type,
539-
* that type will become a member of the extension.)
535+
* We make an extension-membership dependency if we're in an extension
536+
* script and makeExtensionDep is true (and isDependentType isn't true).
537+
* makeExtensionDep should be true when creating a new type or replacing a
538+
* shell type, but not for ALTER TYPE on an existing type. Passing false
539+
* causes the type's extension membership to be left alone.
540+
*
541+
* rebuild should be true if this is a pre-existing type. We will remove
542+
* existing dependencies and rebuild them from scratch. This is needed for
543+
* ALTER TYPE, and also when replacing a shell type. We don't remove any
544+
* existing extension dependency, though (hence, if makeExtensionDep is also
545+
* true and the type belongs to some other extension, an error will occur).
540546
*/
541547
void
542548
GenerateTypeDependencies(HeapTuple typeTuple,
@@ -546,6 +552,7 @@ GenerateTypeDependencies(HeapTuple typeTuple,
546552
char relationKind, /* only for relation rowtypes */
547553
bool isImplicitArray,
548554
bool isDependentType,
555+
bool makeExtensionDep,
549556
bool rebuild)
550557
{
551558
Form_pg_type typeForm = (Form_pg_type) GETSTRUCT(typeTuple);
@@ -602,7 +609,8 @@ GenerateTypeDependencies(HeapTuple typeTuple,
602609
recordDependencyOnNewAcl(TypeRelationId, typeObjectId, 0,
603610
typeForm->typowner, typacl);
604611

605-
recordDependencyOnCurrentExtension(&myself, rebuild);
612+
if (makeExtensionDep)
613+
recordDependencyOnCurrentExtension(&myself, rebuild);
606614
}
607615

608616
/* Normal dependencies on the I/O functions */

src/backend/commands/operatorcmds.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -530,7 +530,7 @@ AlterOperator(AlterOperatorStmt *stmt)
530530

531531
CatalogTupleUpdate(catalog, &tup->t_self, tup);
532532

533-
address = makeOperatorDependencies(tup, true);
533+
address = makeOperatorDependencies(tup, false, true);
534534

535535
InvokeObjectPostAlterHook(OperatorRelationId, oprId, 0);
536536

src/backend/commands/typecmds.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2253,6 +2253,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
22532253
0, /* relation kind is n/a */
22542254
false, /* a domain isn't an implicit array */
22552255
false, /* nor is it any kind of dependent type */
2256+
false, /* don't touch extension membership */
22562257
true); /* We do need to rebuild dependencies */
22572258

22582259
InvokeObjectPostAlterHook(TypeRelationId, domainoid, 0);
@@ -3984,6 +3985,7 @@ AlterTypeRecurse(Oid typeOid, bool isImplicitArray,
39843985
0, /* we rejected composite types above */
39853986
isImplicitArray, /* it might be an array */
39863987
isImplicitArray, /* dependent iff it's array */
3988+
false, /* don't touch extension membership */
39873989
true);
39883990

39893991
InvokeObjectPostAlterHook(TypeRelationId, typeOid, 0);

src/include/catalog/pg_operator.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ extern ObjectAddress OperatorCreate(const char *operatorName,
9595
bool canMerge,
9696
bool canHash);
9797

98-
extern ObjectAddress makeOperatorDependencies(HeapTuple tuple, bool isUpdate);
98+
extern ObjectAddress makeOperatorDependencies(HeapTuple tuple,
99+
bool makeExtensionDep,
100+
bool isUpdate);
99101

100102
extern void OperatorUpd(Oid baseId, Oid commId, Oid negId, bool isDelete);
101103

src/include/catalog/pg_type.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,7 @@ extern void GenerateTypeDependencies(HeapTuple typeTuple,
359359
* rowtypes */
360360
bool isImplicitArray,
361361
bool isDependentType,
362+
bool makeExtensionDep,
362363
bool rebuild);
363364

364365
extern void RenameTypeInternal(Oid typeOid, const char *newTypeName,

0 commit comments

Comments
 (0)