Bogus collation version recording in recordMultipleDependencies - Mailing list pgsql-hackers

From Tom Lane
Subject Bogus collation version recording in recordMultipleDependencies
Date
Msg-id 3564817.1618420687@sss.pgh.pa.us
Whole thread Raw
Responses Re: Bogus collation version recording in recordMultipleDependencies  (Julien Rouhaud <rjuju123@gmail.com>)
List pgsql-hackers
I noticed some broken-looking logic in recordMultipleDependencies
concerning how it records collation versions.  It was a bit harder
than I expected to demonstrate the bugs, but I eventually succeeded
with

u8=# create function foo(varchar) returns bool language sql return false;
CREATE FUNCTION
u8=# create collation mycoll from "en_US";
CREATE COLLATION
u8=# CREATE DOMAIN d4 AS character varying(3) COLLATE "aa_DJ"
    CONSTRAINT yes_or_no_check CHECK (value = 'YES' collate mycoll or foo(value));
CREATE DOMAIN
u8=# select objid, pg_describe_object(classid,objid,objsubid) as obj,
pg_describe_object(refclassid,refobjid,refobjsubid)as ref, deptype, refobjversion from pg_depend where objid =
'd4'::regtype;
 objid |   obj   |        ref        | deptype | refobjversion
-------+---------+-------------------+---------+---------------
 37421 | type d4 | schema public     | n       |
 37421 | type d4 | collation "aa_DJ" | n       |
(2 rows)

u8=# select objid, pg_describe_object(classid,objid,objsubid) as obj,
pg_describe_object(refclassid,refobjid,refobjsubid)as ref, deptype, refobjversion from pg_depend where refobjid =
'd4'::regtype;
 objid |            obj             |   ref   | deptype | refobjversion
-------+----------------------------+---------+---------+---------------
 37420 | type d4[]                  | type d4 | i       |
 37422 | constraint yes_or_no_check | type d4 | a       |
(2 rows)

u8=# select objid, pg_describe_object(classid,objid,objsubid) as obj,
pg_describe_object(refclassid,refobjid,refobjsubid)as ref, deptype, refobjversion from pg_depend where objid = 37422; 
 objid |            obj             |               ref               | deptype | refobjversion
-------+----------------------------+---------------------------------+---------+---------------
 37422 | constraint yes_or_no_check | type d4                         | a       |
 37422 | constraint yes_or_no_check | collation mycoll                | n       | 2.28
 37422 | constraint yes_or_no_check | function foo(character varying) | n       | 2.28
 37422 | constraint yes_or_no_check | collation "default"             | n       |
(4 rows)

(This is in a glibc-based build, with C as the database's default
collation.)

One question here is whether it's correct that the domain's dependency
on collation "aa_DJ" is unversioned.  Maybe that's intentional, but it
seems worth asking.

Anyway, there are two pretty obvious bugs in the dependencies for the
domain's CHECK constraint: the version for collation mycoll leaks
into the entry for function foo, and an entirely useless (because
unversioned) dependency is recorded on the default collation.

... well, it's almost entirely useless.  If we fix things to not do that
(as per patch 0001 below), the results of the create_index regression
test become unstable, because there's two queries that inquire into the
dependencies of indexes, and their results change depending on whether
the default collation has a version or not.  I'd be inclined to just
take out the portions of that test that depend on that question, but
maybe somebody will complain that there's a loss of useful coverage.
I don't agree, but maybe I'll be overruled.

If we do feel we need to stay bug-compatible with that behavior, then
the alternate 0002 patch just fixes the version-leakage-across-entries
problem, while still removing the unnecessary assumption that C, POSIX,
and DEFAULT are the only pinned collations.

(To be clear: 0002 passes check-world as-is, while 0001 is not
committable without some regression-test fiddling.)

Thoughts?

            regards, tom lane

diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 362db7fe91..1217c01b8a 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -73,7 +73,6 @@ recordMultipleDependencies(const ObjectAddress *depender,
                 max_slots,
                 slot_init_count,
                 slot_stored_count;
-    char       *version = NULL;

     if (nreferenced <= 0)
         return;                    /* nothing to do */
@@ -104,31 +103,22 @@ recordMultipleDependencies(const ObjectAddress *depender,
     slot_init_count = 0;
     for (i = 0; i < nreferenced; i++, referenced++)
     {
-        bool        ignore_systempin = false;
+        char       *version = NULL;

         if (record_version)
         {
             /* For now we only know how to deal with collations. */
             if (referenced->classId == CollationRelationId)
             {
-                /* C and POSIX don't need version tracking. */
+                /* These are unversioned, so don't waste cycles on them. */
                 if (referenced->objectId == C_COLLATION_OID ||
                     referenced->objectId == POSIX_COLLATION_OID)
                     continue;

                 version = get_collation_version_for_oid(referenced->objectId,
                                                         false);
-
-                /*
-                 * Default collation is pinned, so we need to force recording
-                 * the dependency to store the version.
-                 */
-                if (referenced->objectId == DEFAULT_COLLATION_OID)
-                    ignore_systempin = true;
             }
         }
-        else
-            Assert(!version);

         /*
          * If the referenced object is pinned by the system, there's no real
@@ -136,7 +126,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
          * version.  This saves lots of space in pg_depend, so it's worth the
          * time taken to check.
          */
-        if (!ignore_systempin && isObjectPinned(referenced, dependDesc))
+        if (version == NULL && isObjectPinned(referenced, dependDesc))
             continue;

         if (slot_init_count < max_slots)
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 362db7fe91..174ce4b7df 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -73,7 +73,6 @@ recordMultipleDependencies(const ObjectAddress *depender,
                 max_slots,
                 slot_init_count,
                 slot_stored_count;
-    char       *version = NULL;

     if (nreferenced <= 0)
         return;                    /* nothing to do */
@@ -104,6 +103,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
     slot_init_count = 0;
     for (i = 0; i < nreferenced; i++, referenced++)
     {
+        char       *version = NULL;
         bool        ignore_systempin = false;

         if (record_version)
@@ -111,7 +111,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
             /* For now we only know how to deal with collations. */
             if (referenced->classId == CollationRelationId)
             {
-                /* C and POSIX don't need version tracking. */
+                /* These are unversioned, so don't waste cycles on them. */
                 if (referenced->objectId == C_COLLATION_OID ||
                     referenced->objectId == POSIX_COLLATION_OID)
                     continue;
@@ -120,15 +120,16 @@ recordMultipleDependencies(const ObjectAddress *depender,
                                                         false);

                 /*
-                 * Default collation is pinned, so we need to force recording
-                 * the dependency to store the version.
+                 * If we have a version, make sure we record it even if the
+                 * collation is pinned.  Also force recording a dependency on
+                 * the "default" collation even if it hasn't got a version;
+                 * this is an ugly kluge to ensure stability of regression
+                 * test results.
                  */
-                if (referenced->objectId == DEFAULT_COLLATION_OID)
+                if (version || referenced->objectId == DEFAULT_COLLATION_OID)
                     ignore_systempin = true;
             }
         }
-        else
-            Assert(!version);

         /*
          * If the referenced object is pinned by the system, there's no real

pgsql-hackers by date:

Previous
From: Robert Haas
Date:
Subject: Re: pg_amcheck contrib application
Next
From: Mark Dilger
Date:
Subject: Re: Converting contrib SQL functions to new style