]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Improve generate_partition_qual()'s cache handling on out-of-memory errors
authorMichael Paquier <michael@paquier.xyz>
Tue, 21 Jul 2026 07:50:18 +0000 (16:50 +0900)
committerMichael Paquier <michael@paquier.xyz>
Tue, 21 Jul 2026 07:50:18 +0000 (16:50 +0900)
An in-flight failure when trying to set rd_partcheckcxt or
rd_partcheck, while for example doing an allocation in copyObject(),
would leave a backend cache in a corrupted state.  The operations are
now ordered so as we avoid a leak in the cache memory context and a
semi-filled cache state when an allocation failure happens.

This is unlikely going to be hit in practice.  Like the other
improvements of this kind, no backpatch is done.

Reported-by: Alexander Lakhin <exclusion@gmail.com>
Author: Matthias van de Meent <boekewurm+postgres@gmail.com>
Discussion: https://postgr.es/m/95c64dc2-3abe-4f4e-b285-4c681f565d9f@gmail.com

src/backend/utils/cache/partcache.c

index 3107075c9ad6cb911ee2a605b52d22348618a54f..a0982674884cf0b6f86169759cf0d993f5f7c006 100644 (file)
@@ -411,14 +411,31 @@ generate_partition_qual(Relation rel)
         */
        if (result != NIL)
        {
-               rel->rd_partcheckcxt = AllocSetContextCreate(CacheMemoryContext,
-                                                                                                        "partition constraint",
-                                                                                                        ALLOCSET_SMALL_SIZES);
-               MemoryContextCopyAndSetIdentifier(rel->rd_partcheckcxt,
+               /*
+                * Take care to order operations so that allocation errors don't leave
+                * the catcache in an invalid state; first allocate everything into a
+                * transactional context, then associate it with CacheContext and
+                * update the relation data.  This also avoids leaking memory if we
+                * ever hit OOM here.
+                */
+               List       *partcheck;
+               MemoryContext partctx;
+
+               partctx = AllocSetContextCreate(CurrentMemoryContext,
+                                                                               "partition constraint",
+                                                                               ALLOCSET_SMALL_SIZES);
+               MemoryContextCopyAndSetIdentifier(partctx,
                                                                                  RelationGetRelationName(rel));
-               oldcxt = MemoryContextSwitchTo(rel->rd_partcheckcxt);
-               rel->rd_partcheck = copyObject(result);
+
+               oldcxt = MemoryContextSwitchTo(partctx);
+               partcheck = copyObject(result);
                MemoryContextSwitchTo(oldcxt);
+
+               /* finally, link the allocations and memctx into the right places */
+               MemoryContextSetParent(partctx, CacheMemoryContext);
+
+               rel->rd_partcheckcxt = partctx;
+               rel->rd_partcheck = partcheck;
        }
        else
                rel->rd_partcheck = NIL;