cannot drop intarray extension
hi.
---- setup
drop table if exist test__int cascade;
create extension intarray;
CREATE TABLE test__int( a int[] );
CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 1));
drop extension intarray cascade;
NOTICE: drop cascades to index text_idx
2024-06-03 11:53:32.629 CST [41165] ERROR: cache lookup failed for
function 17758
2024-06-03 11:53:32.629 CST [41165] STATEMENT: drop extension intarray cascade;
ERROR: cache lookup failed for function 17758
------------------------------------------------
backtrace info:
index_getprocinfo
#0 index_opclass_options (indrel=0x7faeca727b58, attnum=1,
attoptions=94372901674408, validate=false)
at ../../Desktop/pg_src/src4/postgres/src/backend/access/index/indexam.c:1034
#1 0x000055d4e63a79cb in RelationGetIndexAttOptions
(relation=0x7faeca727b58, copy=false)
at ../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:5872
#2 0x000055d4e639d72d in RelationInitIndexAccessInfo (relation=0x7faeca727b58)
at ../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:1569
#3 0x000055d4e639c5ac in RelationBuildDesc (targetRelId=24582, insertIt=true)
at ../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:1207
#4 0x000055d4e639e9ce in RelationIdGetRelation (relationId=24582)
at ../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:2115
#5 0x000055d4e5a412fd in relation_open (relationId=24582, lockmode=8)
at ../../Desktop/pg_src/src4/postgres/src/backend/access/common/relation.c:58
#6 0x000055d4e5ae6a06 in index_open (relationId=24582, lockmode=8)
at ../../Desktop/pg_src/src4/postgres/src/backend/access/index/indexam.c:137
#7 0x000055d4e5be61b8 in index_drop (indexId=24582, concurrent=false,
concurrent_lock_mode=false)
at ../../Desktop/pg_src/src4/postgres/src/backend/catalog/index.c:2156
------------------------
i guess it's because we first dropped the function g_intbig_options
then later we need it.
Hi Jian
On Mon, Jun 3, 2024 at 9:14 AM jian he <jian.universality@gmail.com> wrote:
hi.
---- setup
drop table if exist test__int cascade;
create extension intarray;CREATE TABLE test__int( a int[] );
CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen =
1));
drop extension intarray cascade;
NOTICE: drop cascades to index text_idx
2024-06-03 11:53:32.629 CST [41165] ERROR: cache lookup failed for
function 17758
Its a bug.
Show quoted text
2024-06-03 11:53:32.629 CST [41165] STATEMENT: drop extension intarray
cascade;
ERROR: cache lookup failed for function 17758------------------------------------------------
backtrace info:
index_getprocinfo
#0 index_opclass_options (indrel=0x7faeca727b58, attnum=1,
attoptions=94372901674408, validate=false)
at
../../Desktop/pg_src/src4/postgres/src/backend/access/index/indexam.c:1034
#1 0x000055d4e63a79cb in RelationGetIndexAttOptions
(relation=0x7faeca727b58, copy=false)
at
../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:5872
#2 0x000055d4e639d72d in RelationInitIndexAccessInfo
(relation=0x7faeca727b58)
at
../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:1569
#3 0x000055d4e639c5ac in RelationBuildDesc (targetRelId=24582,
insertIt=true)
at
../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:1207
#4 0x000055d4e639e9ce in RelationIdGetRelation (relationId=24582)
at
../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:2115
#5 0x000055d4e5a412fd in relation_open (relationId=24582, lockmode=8)
at
../../Desktop/pg_src/src4/postgres/src/backend/access/common/relation.c:58
#6 0x000055d4e5ae6a06 in index_open (relationId=24582, lockmode=8)
at
../../Desktop/pg_src/src4/postgres/src/backend/access/index/indexam.c:137
#7 0x000055d4e5be61b8 in index_drop (indexId=24582, concurrent=false,
concurrent_lock_mode=false)
at ../../Desktop/pg_src/src4/postgres/src/backend/catalog/index.c:2156
------------------------
i guess it's because we first dropped the function g_intbig_options
then later we need it.
On Mon, Jun 3, 2024 at 12:14 PM jian he <jian.universality@gmail.com> wrote:
hi.
---- setup
drop table if exist test__int cascade;
create extension intarray;CREATE TABLE test__int( a int[] );
CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 1));
drop extension intarray cascade;
NOTICE: drop cascades to index text_idx
2024-06-03 11:53:32.629 CST [41165] ERROR: cache lookup failed for
function 17758
2024-06-03 11:53:32.629 CST [41165] STATEMENT: drop extension intarray cascade;
ERROR: cache lookup failed for function 17758
------------------------------------------------
extension (ltree, pg_trgm) also have the same problem.
drop table if exists t2 cascade;
CREATE EXTENSION ltree;
CREATE TABLE t2 (t ltree);
create index tstidx on t2 using gist (t gist_ltree_ops(siglen=4));
drop extension ltree cascade;
drop table if exists t3 cascade;
CREATE EXTENSION pg_trgm;
CREATE TABLE t3(t text COLLATE "C");
create index trgm_idx on t3 using gist (t gist_trgm_ops(siglen=1));
drop extension pg_trgm cascade;
------------------------------------------------
extension hstore work as expected, no error.
drop table if exists t1 cascade;
create extension hstore;
CREATE TABLE t1 (h hstore);
create index hidx on t1 using gist(h gist_hstore_ops(siglen=1));
drop extension hstore cascade;
on the master branch. i didn't test on other branches.
On Mon, Jun 3, 2024 at 12:14 PM jian he <jian.universality@gmail.com> wrote:
hi.
---- setup
drop table if exist test__int cascade;
create extension intarray;CREATE TABLE test__int( a int[] );
CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 1));
drop extension intarray cascade;
NOTICE: drop cascades to index text_idx
2024-06-03 11:53:32.629 CST [41165] ERROR: cache lookup failed for
function 17758
2024-06-03 11:53:32.629 CST [41165] STATEMENT: drop extension intarray cascade;
ERROR: cache lookup failed for function 17758------------------------------------------------
backtrace info:
index_getprocinfo
#0 index_opclass_options (indrel=0x7faeca727b58, attnum=1,
attoptions=94372901674408, validate=false)
at ../../Desktop/pg_src/src4/postgres/src/backend/access/index/indexam.c:1034
#1 0x000055d4e63a79cb in RelationGetIndexAttOptions
(relation=0x7faeca727b58, copy=false)
at ../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:5872
#2 0x000055d4e639d72d in RelationInitIndexAccessInfo (relation=0x7faeca727b58)
at ../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:1569
#3 0x000055d4e639c5ac in RelationBuildDesc (targetRelId=24582, insertIt=true)
at ../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:1207
#4 0x000055d4e639e9ce in RelationIdGetRelation (relationId=24582)
at ../../Desktop/pg_src/src4/postgres/src/backend/utils/cache/relcache.c:2115
#5 0x000055d4e5a412fd in relation_open (relationId=24582, lockmode=8)
at ../../Desktop/pg_src/src4/postgres/src/backend/access/common/relation.c:58
#6 0x000055d4e5ae6a06 in index_open (relationId=24582, lockmode=8)
at ../../Desktop/pg_src/src4/postgres/src/backend/access/index/indexam.c:137
#7 0x000055d4e5be61b8 in index_drop (indexId=24582, concurrent=false,
concurrent_lock_mode=false)
at ../../Desktop/pg_src/src4/postgres/src/backend/catalog/index.c:2156
------------------------
i guess it's because we first dropped the function g_intbig_options
in this context, the index "text_idx" has a normal dependency with pg_opclass.
but `drop extension intarray cascade;`,
CASCADE means that we drop the pg_opclass and pg_opclass's inner dependency
first, then drop the index.
while drop index (sub functions
RelationGetIndexAttOptions,index_opclass_options, index_getprocinfo)
requires that pg_opclass and its inner dependencies (namely
g_intbig_options, g_int_options) are not dropped first.
in deleteObjectsInList, under certain conditions trying to sort the to
be deleted object list
by just using sort_object_addresses seems to work,
but it looks like a hack.
maybe the proper fix would be in findDependentObjects.
Attachments:
v1-0001-trying-to-resolve-drop-extension-deletion-order.patchapplication/x-patch; name=v1-0001-trying-to-resolve-drop-extension-deletion-order.patchDownload
From 8deefb638df270cf26e5649b1a99f218474821fa Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Fri, 7 Jun 2024 11:25:03 +0800
Subject: [PATCH v1 1/1] trying to resolve drop extension deletion order
sometimes, drop extension cascade cannot resolve the internal deletion order correctly.
e.g.
drop table if exist test__int cascade;
create extension intarray;
CREATE TABLE test__int( a int[] );
CREATE INDEX text_idx on test__int using gist (a gist__intbig_ops(siglen = 1));
drop extension intarray cascade;
----
the index "text_idx" only have a normal dependency with pg_opclass.
even though the index can be dropped separately without affecting the "pg_opclass".
but CASCADE means that we drop the pg_opclass and pg_opclass's inner dependency
first, then drop the index.
while drop index (sub functions RelationGetIndexAttOptions,index_opclass_options, index_getprocinfo)
requires that pg_opclass and its inner dependencies are not dropped first.
Resorting the deleted objects in deleteObjectsInList using sort_object_addresses seems like a hack.
but it works for now.
discussion: https://www.postgresql.org/message-id/CACJufxEspPKC7oxVLci7oUddUmcAGNKJnWWSD7-B03bGtT9gDg%40mail.gmail.com
---
src/backend/catalog/dependency.c | 33 +++++++++++++++++++++++++++----
src/backend/catalog/pg_shdepend.c | 2 +-
src/backend/commands/dropcmds.c | 2 +-
src/backend/commands/indexcmds.c | 2 +-
src/backend/commands/tablecmds.c | 13 +++++++-----
src/include/catalog/dependency.h | 2 +-
6 files changed, 41 insertions(+), 13 deletions(-)
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index d4b5b2ad..d0c2454b 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -183,7 +183,7 @@ static void DeleteInitPrivs(const ObjectAddress *object);
*/
static void
deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,
- int flags)
+ int flags, bool sort_objects)
{
int i;
@@ -213,6 +213,8 @@ deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel,
}
}
+ if (sort_objects)
+ sort_object_addresses(targetObjects);
/*
* Delete all the objects in the proper order, except that if told to, we
* should skip the original object(s).
@@ -311,7 +313,7 @@ performDeletion(const ObjectAddress *object,
object);
/* do the deed */
- deleteObjectsInList(targetObjects, &depRel, flags);
+ deleteObjectsInList(targetObjects, &depRel, flags, false);
/* And clean up */
free_object_addresses(targetObjects);
@@ -330,16 +332,39 @@ performDeletion(const ObjectAddress *object,
*/
void
performMultipleDeletions(const ObjectAddresses *objects,
- DropBehavior behavior, int flags)
+ DropBehavior behavior, int flags, bool sortable)
{
Relation depRel;
ObjectAddresses *targetObjects;
int i;
+ bool sort_objects = true;
+ char relkind = '\0';
/* No work if no objects... */
if (objects->numrefs <= 0)
return;
+ for (int j = 0; j < objects->numrefs; j++)
+ {
+ const ObjectAddress *thisobj = objects->refs + j;
+ if (thisobj->classId != RelationRelationId)
+ continue;
+ relkind = get_rel_relkind(thisobj->objectId);
+
+ /*
+ * for partitioned table, sequence findDependentObjects can resolve
+ * the delete objects order correctly, othercase, we can sort the
+ * to be deleted objects via sort_object_addresses in deleteObjectsInList
+ */
+ if (relkind == RELKIND_PARTITIONED_TABLE || relkind == RELKIND_SEQUENCE)
+ {
+ sort_objects = false;
+ break;
+ }
+ }
+
+ if (!(sortable && sort_objects))
+ sort_objects = false;
/*
* We save some cycles by opening pg_depend just once and passing the
* Relation pointer down to all the recursive deletion steps.
@@ -387,7 +412,7 @@ performMultipleDeletions(const ObjectAddresses *objects,
(objects->numrefs == 1 ? objects->refs : NULL));
/* do the deed */
- deleteObjectsInList(targetObjects, &depRel, flags);
+ deleteObjectsInList(targetObjects, &depRel, flags, sort_objects);
/* And clean up */
free_object_addresses(targetObjects);
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index 20bcfd77..8aef6596 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -1498,7 +1498,7 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
sort_object_addresses(deleteobjs);
/* the dependency mechanism does the actual work */
- performMultipleDeletions(deleteobjs, behavior, 0);
+ performMultipleDeletions(deleteobjs, behavior, 0, true);
table_close(sdepRel, RowExclusiveLock);
diff --git a/src/backend/commands/dropcmds.c b/src/backend/commands/dropcmds.c
index 85eec7e3..7aadd71b 100644
--- a/src/backend/commands/dropcmds.c
+++ b/src/backend/commands/dropcmds.c
@@ -120,7 +120,7 @@ RemoveObjects(DropStmt *stmt)
}
/* Here we really delete them. */
- performMultipleDeletions(objects, stmt->behavior, 0);
+ performMultipleDeletions(objects, stmt->behavior, 0, true);
free_object_addresses(objects);
}
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 309389e2..d70a112a 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -4209,7 +4209,7 @@ ReindexRelationConcurrently(const ReindexStmt *stmt, Oid relationOid, const Rein
* right lock level.
*/
performMultipleDeletions(objects, DROP_RESTRICT,
- PERFORM_DELETION_CONCURRENT_LOCK | PERFORM_DELETION_INTERNAL);
+ PERFORM_DELETION_CONCURRENT_LOCK | PERFORM_DELETION_INTERNAL, true);
}
PopActiveSnapshot();
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 7b6c69b7..ab005cc2 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1459,6 +1459,7 @@ RemoveRelations(DropStmt *drop)
ListCell *cell;
int flags = 0;
LOCKMODE lockmode = AccessExclusiveLock;
+ bool sortable = true;
/* DROP CONCURRENTLY uses a weaker lock, and has some restrictions */
if (drop->concurrent)
@@ -1604,7 +1605,9 @@ RemoveRelations(DropStmt *drop)
add_exact_object_address(&obj, objects);
}
- performMultipleDeletions(objects, drop->behavior, flags);
+ if (list_length(drop->objects) > 1)
+ sortable = false;
+ performMultipleDeletions(objects, drop->behavior, flags, sortable);
free_object_addresses(objects);
}
@@ -9100,7 +9103,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
if (!recursing)
{
/* Recursion has ended, drop everything that was collected */
- performMultipleDeletions(addrs, behavior, 0);
+ performMultipleDeletions(addrs, behavior, 0, true);
free_object_addresses(addrs);
}
@@ -13812,7 +13815,7 @@ ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab, LOCKMODE lockmode)
* It should be okay to use DROP_RESTRICT here, since nothing else should
* be depending on these objects.
*/
- performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+ performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL, true);
free_object_addresses(objects);
@@ -17448,7 +17451,7 @@ PreCommit_on_commit_actions(void)
* by the user, we pass the PERFORM_DELETION_INTERNAL flag.
*/
performMultipleDeletions(targetObjects, DROP_CASCADE,
- PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY);
+ PERFORM_DELETION_INTERNAL | PERFORM_DELETION_QUIETLY, true);
PopActiveSnapshot();
@@ -19488,7 +19491,7 @@ DropClonedTriggersFromPartition(Oid partitionId)
/* make the dependency removal visible to the deletion below */
CommandCounterIncrement();
- performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL);
+ performMultipleDeletions(objects, DROP_RESTRICT, PERFORM_DELETION_INTERNAL, true);
/* done */
free_object_addresses(objects);
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 7eee66f8..f9739f87 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -108,7 +108,7 @@ extern void performDeletion(const ObjectAddress *object,
DropBehavior behavior, int flags);
extern void performMultipleDeletions(const ObjectAddresses *objects,
- DropBehavior behavior, int flags);
+ DropBehavior behavior, int flags, bool sortable);
extern void recordDependencyOnExpr(const ObjectAddress *depender,
Node *expr, List *rtable,
--
2.34.1
On Fri, Jun 07, 2024 at 11:32:14AM +0800, jian he wrote:
in deleteObjectsInList, under certain conditions trying to sort the to
be deleted object list
by just using sort_object_addresses seems to work,
but it looks like a hack.
maybe the proper fix would be in findDependentObjects.
@@ -1459,6 +1459,7 @@ RemoveRelations(DropStmt *drop)
[...]
- performMultipleDeletions(objects, drop->behavior, flags);
+ if (list_length(drop->objects) > 1)
+ sortable = false;
I have not studied the patch in details, but this looks
overcomplicated to me. All the callers of performMultipleDeletions
pass down sortable as true, while deleteObjectsInList() uses this
argument to avoid the sorting on nested calls. It seems to me that
this could be simpler.
--
Michael
Michael Paquier <michael@paquier.xyz> writes:
On Fri, Jun 07, 2024 at 11:32:14AM +0800, jian he wrote:
in deleteObjectsInList, under certain conditions trying to sort the to
be deleted object list
by just using sort_object_addresses seems to work,
but it looks like a hack.
maybe the proper fix would be in findDependentObjects.
I have not studied the patch in details, but this looks
overcomplicated to me.
I dunno about overcomplicated, but it's fundamentally the wrong thing:
it won't do much except move the problem from this example to other
example(s). The difficulty is precisely that we cannot simply delete
objects in reverse OID order and expect that to be OK. It appears to
work in simple cases because reverse OID order usually means deleting
newest objects first, and that usually means dropping depender objects
before dependees --- but dependencies added as a result of later ALTER
commands may not be honored correctly. Not to mention that you can
lose if an OID wraparound happened during the sequence of object
creations.
In the case at hand, the reason we're having trouble with
g_intbig_options() is that the sequence of extension scripts
run by CREATE EXTENSION creates the gist__intbig_ops opfamily
first, and then creates g_intbig_options() and attaches it to
the opfamily later (in intarray--1.2--1.3.sql). So g_intbig_options()
has a larger OID than the opclass that the index depends on.
In DROP EXTENSION, the first level of findDependentObjects() finds
all the direct dependencies (members) of the extension, and then
sorts them by OID descending, concluding that g_intbig_options()
can be dropped before the opclass. Subsequent recursive levels
will find the index and recognize that it must be dropped before
the opclass --- but this fails to account for the fact that we'd
better drop the opclass before any of the functions it depends on.
At some point along the line, we will come across the dependency
that says so; but we don't do anything in response, because if
findDependentObjects() sees that the current object is already
in targetObjects then it thinks it's done.
I think when I wrote this code I was assuming that the dependency-
order traversal performed by findDependentObjects() was sufficient
to guarantee producing a safe deletion order, but it's now obvious
that that's not so. At minimum, when findDependentObjects() finds
that a dependency exists on an object that's already in targetObjects,
it'd need to do something about moving that object to after the one
it's working on. But I doubt we can fix it with just that, because
that won't be enough to handle indirect dependencies.
It looks to me that the only real fix will require performing a
topological sort, similar to what pg_dump does, to produce a safe
deletion order that honors all the direct and indirect dependencies
found by findDependentObjects().
An open question is whether we will need dependency-loop-breaking
logic, or whether the hackery done in findDependentObjects() is
sufficient to ensure that we can assume there are no loops in the
dependencies it chooses to output. It might be a better idea to
get rid of that logic and instead have explicit loop-breaking like
the way pg_dump does it.
It's also tempting to wonder if we can share code for this with
pg_dump. The TopoSort function alone is not that big, but if we
have to duplicate the loop-breaking logic it'd get annoying.
Anyway, this is a very long-standing problem and I don't think
we should try to solve it in a rush.
regards, tom lane