Re: BUG #18014: Releasing catcache entries makes schema_to_xmlschema() fail when parallel workers are used

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: BUG #18014: Releasing catcache entries makes schema_to_xmlschema() fail when parallel workers are used
Дата
Msg-id 1389919.1697144487@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: BUG #18014: Releasing catcache entries makes schema_to_xmlschema() fail when parallel workers are used  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: BUG #18014: Releasing catcache entries makes schema_to_xmlschema() fail when parallel workers are used  (Alexander Lakhin <exclusion@gmail.com>)
Список pgsql-bugs
I wrote:
> Michael Paquier <michael@paquier.xyz> writes:
>> FWIW, I'm having a hard time thinking about a reason that we should
>> not support SearchSysCacheExists()+lookup to be a valid pattern, even
>> if the cache is clobbered.  I am pretty sure that there are other code
>> paths in the tree, not mentioned on this thread, that do exactly that
>> (haven't checked, but indexcmds.c is one coming in mind).

> Yeah.  As I see it, there are several questions here.

> First, is the SearchSysCacheExists()+lookup pattern actually unsafe,
> that is is there a way to break it without any cache-clobbering debug
> options enabled?  If so, there's no question we have to get rid of it.

I came back to this question today, and after more thought I'm going
to cast my vote for "let's not trust SearchSysCacheExists()+lookup".
It just seems like that's too fragile.  The case reported in this
thread of it failing in parallel workers but not main should scare
anybody, and the future course of development seems far more likely
to introduce new problems than remove them.

I spent some time looking through existing SearchSysCacheExists calls,
and I could only find two sets of routines where we seem to be
depending on SearchSysCacheExists to protect a subsequent lookup
somewhere else, and there isn't any lock on the object in question.
Those are the has_foo_privilege functions discussed here, and the
foo_is_visible functions near the bottom of namespace.c.  I'm not
sure why we've not heard complaints traceable to the foo_is_visible
family.  Maybe nobody has tried hard to break them, or maybe they
are just less likely to be used in ways that are at risk.

It turns out to not be that hard to get rid of the problem in the
has_foo_privilege family, as per attached patches.  I've not looked
at fixing the foo_is_visible family, but that probably ought to be a
separate patch anyway.

BTW, while nosing around I found what seems like a very nasty related
bug.  Suppose that a catalog tuple being loaded into syscache contains
some toasted fields.  CatalogCacheCreateEntry will flatten the tuple,
involving fetches from toast tables that will certainly cause
AcceptInvalidationMessages calls.  What if one of those should have
invalidated this tuple?  We will not notice, because it's not in
the hashtable yet.  When we do add it, we will mark it not-dead,
meaning that the stale entry looks fine and could persist for a long
while.

Anyway, as to the attached patches: I split this into two parts
just to make it easier to review.  0001 deals with the functions
that use pg_class_aclcheck and related, while 0002 deals with the
ones that go through object_aclcheck.  In both cases, we're
basically extending the API convention somebody added awhile ago
that foo_aclcheck and foo_aclmask should have extended versions
foo_aclcheck_ext and foo_aclmask_ext that take an "is_missing"
argument.  That wasn't terribly well documented IMO, so 0001
starts out with massaging the comments to make it clearer.
I also changed some existing places in pg_attribute_aclmask_ext and
pg_attribute_aclcheck_all to conform to the is_missing convention,
rather than hard-wiring a decision that it's okay to ignore lookup
failure.

pg_attribute_aclcheck_all also had a most curious decision that
it could call pg_attribute_aclmask, which not only opens it right
back up to potential lookup failure but is quite inefficient,
requiring in three syscache lookups instead of one for each
column having a non-null attacl.  It just takes a few more lines
to call aclmask() directly.

0001's changes in acl.c are straightforward, but it's worth noting
that the has_sequence_privilege functions hadn't gotten the memo
about not failing when a bogus relation OID is passed.

0002 is pretty straightforward as well, just adding an "_ext"
version of object_aclcheck/object_aclmask and using those
where appropriate.

It looks like 0001 could be back-patched as far as v14 without
too much trouble.  Before that, there isn't pg_class_aclcheck_ext,
and I'm not sure it's worth the trouble to add.  0002 will only
work in HEAD and v16 because object_aclcheck wasn't there earlier.
I'm not sure whether to bother back-patching at all, given that
we've only seen this problem in test builds.

Comments?

            regards, tom lane

diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index d1f5dcd8be..95b0f8c7b0 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -13,6 +13,27 @@
  * NOTES
  *      See acl.h.
  *
+ *      The xxx_aclmask() functions in this file are wrappers around
+ *      acl.c's aclmask() function; see that for basic usage information.
+ *      The wrapper functions add object-type-specific lookup capability.
+ *      Generally, they will throw error if the object doesn't exist.
+ *
+ *      The xxx_aclmask_ext() functions add the ability to not throw
+ *      error if the object doesn't exist.  If their "is_missing" argument
+ *      isn't NULL, then when the object isn't found they will set
+ *      *is_missing = true and return zero (no privileges) instead of
+ *      throwing an error.  Caller must initialize *is_missing = false.
+ *
+ *      The xxx_aclcheck() functions are simplified wrappers around the
+ *      corresponding xxx_aclmask() functions, simply returning ACLCHECK_OK
+ *      if any of the privileges specified in "mode" are held, and otherwise
+ *      a suitable error code (in practice, always ACLCHECK_NO_PRIV).
+ *      Again, they will throw error if the object doesn't exist.
+ *
+ *      The xxx_aclcheck_ext() functions add the ability to not throw
+ *      error if the object doesn't exist.  Their "is_missing" argument
+ *      works similarly to the xxx_aclmask_ext() functions.
+ *
  *-------------------------------------------------------------------------
  */
 #include "postgres.h"
@@ -3149,10 +3170,7 @@ pg_attribute_aclmask(Oid table_oid, AttrNumber attnum, Oid roleid,
 }

 /*
- * Routine for examining a user's privileges for a column
- *
- * Does the bulk of the work for pg_attribute_aclmask(), and allows other
- * callers to avoid the missing attribute ERROR when is_missing is non-NULL.
+ * Routine for examining a user's privileges for a column, with is_missing
  */
 static AclMode
 pg_attribute_aclmask_ext(Oid table_oid, AttrNumber attnum, Oid roleid,
@@ -3226,15 +3244,24 @@ pg_attribute_aclmask_ext(Oid table_oid, AttrNumber attnum, Oid roleid,
      * Must get the relation's ownerId from pg_class.  Since we already found
      * a pg_attribute entry, the only likely reason for this to fail is that a
      * concurrent DROP of the relation committed since then (which could only
-     * happen if we don't have lock on the relation).  We prefer to report "no
-     * privileges" rather than failing in such a case, so as to avoid unwanted
-     * failures in has_column_privilege() tests.
+     * happen if we don't have lock on the relation).  Treat that similarly to
+     * not finding the attribute entry.
      */
     classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
     if (!HeapTupleIsValid(classTuple))
     {
         ReleaseSysCache(attTuple);
-        return 0;
+        if (is_missing != NULL)
+        {
+            /* return "no privileges" instead of throwing an error */
+            *is_missing = true;
+            return 0;
+        }
+        else
+            ereport(ERROR,
+                    (errcode(ERRCODE_UNDEFINED_TABLE),
+                     errmsg("relation with OID %u does not exist",
+                            table_oid)));
     }
     classForm = (Form_pg_class) GETSTRUCT(classTuple);

@@ -3267,10 +3294,7 @@ pg_class_aclmask(Oid table_oid, Oid roleid,
 }

 /*
- * Routine for examining a user's privileges for a table
- *
- * Does the bulk of the work for pg_class_aclmask(), and allows other
- * callers to avoid the missing relation ERROR when is_missing is non-NULL.
+ * Routine for examining a user's privileges for a table, with is_missing
  */
 static AclMode
 pg_class_aclmask_ext(Oid table_oid, Oid roleid, AclMode mask,
@@ -3784,10 +3808,8 @@ pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,


 /*
- * Exported routine for checking a user's access privileges to a column
- *
- * Does the bulk of the work for pg_attribute_aclcheck(), and allows other
- * callers to avoid the missing attribute ERROR when is_missing is non-NULL.
+ * Exported routine for checking a user's access privileges to a column,
+ * with is_missing
  */
 AclResult
 pg_attribute_aclcheck_ext(Oid table_oid, AttrNumber attnum,
@@ -3822,23 +3844,47 @@ pg_attribute_aclcheck_ext(Oid table_oid, AttrNumber attnum,
 AclResult
 pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
                           AclMaskHow how)
+{
+    return pg_attribute_aclcheck_all_ext(table_oid, roleid, mode, how, NULL);
+}
+
+/*
+ * Exported routine for checking a user's access privileges to any/all columns,
+ * with is_missing
+ */
+AclResult
+pg_attribute_aclcheck_all_ext(Oid table_oid, Oid roleid,
+                              AclMode mode, AclMaskHow how,
+                              bool *is_missing)
 {
     AclResult    result;
     HeapTuple    classTuple;
     Form_pg_class classForm;
+    Oid            ownerId;
     AttrNumber    nattrs;
     AttrNumber    curr_att;

     /*
-     * Must fetch pg_class row to check number of attributes.  As in
-     * pg_attribute_aclmask, we prefer to return "no privileges" instead of
-     * throwing an error if we get any unexpected lookup errors.
+     * Must fetch pg_class row to get owner ID and number of attributes.
      */
     classTuple = SearchSysCache1(RELOID, ObjectIdGetDatum(table_oid));
     if (!HeapTupleIsValid(classTuple))
-        return ACLCHECK_NO_PRIV;
+    {
+        if (is_missing != NULL)
+        {
+            /* return "no privileges" instead of throwing an error */
+            *is_missing = true;
+            return ACLCHECK_NO_PRIV;
+        }
+        else
+            ereport(ERROR,
+                    (errcode(ERRCODE_UNDEFINED_TABLE),
+                     errmsg("relation with OID %u does not exist",
+                            table_oid)));
+    }
     classForm = (Form_pg_class) GETSTRUCT(classTuple);

+    ownerId = classForm->relowner;
     nattrs = classForm->relnatts;

     ReleaseSysCache(classTuple);
@@ -3852,11 +3898,20 @@ pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
     for (curr_att = 1; curr_att <= nattrs; curr_att++)
     {
         HeapTuple    attTuple;
+        Datum        aclDatum;
+        bool        isNull;
+        Acl           *acl;
         AclMode        attmask;

         attTuple = SearchSysCache2(ATTNUM,
                                    ObjectIdGetDatum(table_oid),
                                    Int16GetDatum(curr_att));
+
+        /*
+         * Lookup failure probably indicates that the table was just dropped,
+         * but we'll treat it the same as a dropped column rather than
+         * throwing error.
+         */
         if (!HeapTupleIsValid(attTuple))
             continue;

@@ -3867,16 +3922,27 @@ pg_attribute_aclcheck_all(Oid table_oid, Oid roleid, AclMode mode,
             continue;
         }

+        aclDatum = SysCacheGetAttr(ATTNUM, attTuple, Anum_pg_attribute_attacl,
+                                   &isNull);
+
         /*
          * Here we hard-wire knowledge that the default ACL for a column
          * grants no privileges, so that we can fall out quickly in the very
          * common case where attacl is null.
          */
-        if (heap_attisnull(attTuple, Anum_pg_attribute_attacl, NULL))
+        if (isNull)
             attmask = 0;
         else
-            attmask = pg_attribute_aclmask(table_oid, curr_att, roleid,
-                                           mode, ACLMASK_ANY);
+        {
+            /* detoast column's ACL if necessary */
+            acl = DatumGetAclP(aclDatum);
+
+            attmask = aclmask(acl, roleid, ownerId, mode, ACLMASK_ANY);
+
+            /* if we have a detoasted copy, free it */
+            if ((Pointer) acl != DatumGetPointer(aclDatum))
+                pfree(acl);
+        }

         ReleaseSysCache(attTuple);

@@ -3911,10 +3977,8 @@ pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode)
 }

 /*
- * Exported routine for checking a user's access privileges to a table
- *
- * Does the bulk of the work for pg_class_aclcheck(), and allows other
- * callers to avoid the missing relation ERROR when is_missing is non-NULL.
+ * Exported routine for checking a user's access privileges to a table,
+ * with is_missing
  */
 AclResult
 pg_class_aclcheck_ext(Oid table_oid, Oid roleid,
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 27eabb80ab..809cb6f03f 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -1915,14 +1915,15 @@ has_table_privilege_name_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = get_role_oid_or_public(NameStr(*username));
     mode = convert_table_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
-        PG_RETURN_NULL();
+    aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);

-    aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -1941,14 +1942,15 @@ has_table_privilege_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = GetUserId();
     mode = convert_table_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
-        PG_RETURN_NULL();
+    aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);

-    aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -1989,13 +1991,14 @@ has_table_privilege_id_id(PG_FUNCTION_ARGS)
     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     mode = convert_table_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
-        PG_RETURN_NULL();
+    aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);

-    aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -2134,6 +2137,7 @@ has_sequence_privilege_name_id(PG_FUNCTION_ARGS)
     AclMode        mode;
     AclResult    aclresult;
     char        relkind;
+    bool        is_missing = false;

     roleid = get_role_oid_or_public(NameStr(*username));
     mode = convert_sequence_priv_string(priv_type_text);
@@ -2146,7 +2150,10 @@ has_sequence_privilege_name_id(PG_FUNCTION_ARGS)
                  errmsg("\"%s\" is not a sequence",
                         get_rel_name(sequenceoid))));

-    aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
+    aclresult = pg_class_aclcheck_ext(sequenceoid, roleid, mode, &is_missing);
+
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -2166,6 +2173,7 @@ has_sequence_privilege_id(PG_FUNCTION_ARGS)
     AclMode        mode;
     AclResult    aclresult;
     char        relkind;
+    bool        is_missing = false;

     roleid = GetUserId();
     mode = convert_sequence_priv_string(priv_type_text);
@@ -2178,7 +2186,11 @@ has_sequence_privilege_id(PG_FUNCTION_ARGS)
                  errmsg("\"%s\" is not a sequence",
                         get_rel_name(sequenceoid))));

-    aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
+
+    aclresult = pg_class_aclcheck_ext(sequenceoid, roleid, mode, &is_missing);
+
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -2225,6 +2237,7 @@ has_sequence_privilege_id_id(PG_FUNCTION_ARGS)
     AclMode        mode;
     AclResult    aclresult;
     char        relkind;
+    bool        is_missing = false;

     mode = convert_sequence_priv_string(priv_type_text);
     relkind = get_rel_relkind(sequenceoid);
@@ -2236,7 +2249,11 @@ has_sequence_privilege_id_id(PG_FUNCTION_ARGS)
                  errmsg("\"%s\" is not a sequence",
                         get_rel_name(sequenceoid))));

-    aclresult = pg_class_aclcheck(sequenceoid, roleid, mode);
+
+    aclresult = pg_class_aclcheck_ext(sequenceoid, roleid, mode, &is_missing);
+
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -2345,18 +2362,22 @@ has_any_column_privilege_name_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = get_role_oid_or_public(NameStr(*username));
     mode = convert_column_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
-        PG_RETURN_NULL();
-
     /* First check at table level, then examine each column if needed */
-    aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+    aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
     if (aclresult != ACLCHECK_OK)
-        aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
-                                              ACLMASK_ANY);
+    {
+        if (is_missing)
+            PG_RETURN_NULL();
+        aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
+                                                  ACLMASK_ANY, &is_missing);
+        if (is_missing)
+            PG_RETURN_NULL();
+    }

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -2375,18 +2396,22 @@ has_any_column_privilege_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = GetUserId();
     mode = convert_column_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
-        PG_RETURN_NULL();
-
     /* First check at table level, then examine each column if needed */
-    aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+    aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
     if (aclresult != ACLCHECK_OK)
-        aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
-                                              ACLMASK_ANY);
+    {
+        if (is_missing)
+            PG_RETURN_NULL();
+        aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
+                                                  ACLMASK_ANY, &is_missing);
+        if (is_missing)
+            PG_RETURN_NULL();
+    }

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -2431,17 +2456,21 @@ has_any_column_privilege_id_id(PG_FUNCTION_ARGS)
     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     mode = convert_column_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(tableoid)))
-        PG_RETURN_NULL();
-
     /* First check at table level, then examine each column if needed */
-    aclresult = pg_class_aclcheck(tableoid, roleid, mode);
+    aclresult = pg_class_aclcheck_ext(tableoid, roleid, mode, &is_missing);
     if (aclresult != ACLCHECK_OK)
-        aclresult = pg_attribute_aclcheck_all(tableoid, roleid, mode,
-                                              ACLMASK_ANY);
+    {
+        if (is_missing)
+            PG_RETURN_NULL();
+        aclresult = pg_attribute_aclcheck_all_ext(tableoid, roleid, mode,
+                                                  ACLMASK_ANY, &is_missing);
+        if (is_missing)
+            PG_RETURN_NULL();
+    }

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index aba1afa971..331a87d0e6 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -250,6 +250,9 @@ extern AclResult pg_attribute_aclcheck_ext(Oid table_oid, AttrNumber attnum,
                                            bool *is_missing);
 extern AclResult pg_attribute_aclcheck_all(Oid table_oid, Oid roleid,
                                            AclMode mode, AclMaskHow how);
+extern AclResult pg_attribute_aclcheck_all_ext(Oid table_oid, Oid roleid,
+                                               AclMode mode, AclMaskHow how,
+                                               bool *is_missing);
 extern AclResult pg_class_aclcheck(Oid table_oid, Oid roleid, AclMode mode);
 extern AclResult pg_class_aclcheck_ext(Oid table_oid, Oid roleid,
                                        AclMode mode, bool *is_missing);
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index 95b0f8c7b0..67354cea92 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -160,6 +160,9 @@ static AclMode pg_aclmask(ObjectType objtype, Oid object_oid, AttrNumber attnum,
                           Oid roleid, AclMode mask, AclMaskHow how);
 static AclMode object_aclmask(Oid classid, Oid objectid, Oid roleid,
                               AclMode mask, AclMaskHow how);
+static AclMode object_aclmask_ext(Oid classid, Oid objectid, Oid roleid,
+                                  AclMode mask, AclMaskHow how,
+                                  bool *is_missing);
 static AclMode pg_attribute_aclmask(Oid table_oid, AttrNumber attnum,
                                     Oid roleid, AclMode mask, AclMaskHow how);
 static AclMode pg_attribute_aclmask_ext(Oid table_oid, AttrNumber attnum,
@@ -172,10 +175,12 @@ static AclMode pg_parameter_acl_aclmask(Oid acl_oid, Oid roleid,
                                         AclMode mask, AclMaskHow how);
 static AclMode pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
                                                AclMode mask, AclMaskHow how, Snapshot snapshot);
-static AclMode pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
-                                    AclMode mask, AclMaskHow how);
-static AclMode pg_type_aclmask(Oid type_oid, Oid roleid,
-                               AclMode mask, AclMaskHow how);
+static AclMode pg_namespace_aclmask_ext(Oid nsp_oid, Oid roleid,
+                                        AclMode mask, AclMaskHow how,
+                                        bool *is_missing);
+static AclMode pg_type_aclmask_ext(Oid type_oid, Oid roleid,
+                                   AclMode mask, AclMaskHow how,
+                                   bool *is_missing);
 static void recordExtensionInitPriv(Oid objoid, Oid classoid, int objsubid,
                                     Acl *new_acl);
 static void recordExtensionInitPrivWorker(Oid objoid, Oid classoid, int objsubid,
@@ -3085,6 +3090,18 @@ pg_aclmask(ObjectType objtype, Oid object_oid, AttrNumber attnum, Oid roleid,
 static AclMode
 object_aclmask(Oid classid, Oid objectid, Oid roleid,
                AclMode mask, AclMaskHow how)
+{
+    return object_aclmask_ext(classid, objectid, roleid, mask, how, NULL);
+}
+
+/*
+ * Generic routine for examining a user's privileges for an object,
+ * with is_missing
+ */
+static AclMode
+object_aclmask_ext(Oid classid, Oid objectid, Oid roleid,
+                   AclMode mask, AclMaskHow how,
+                   bool *is_missing)
 {
     int            cacheid;
     AclMode        result;
@@ -3098,9 +3115,11 @@ object_aclmask(Oid classid, Oid objectid, Oid roleid,
     switch (classid)
     {
         case NamespaceRelationId:
-            return pg_namespace_aclmask(objectid, roleid, mask, how);
+            return pg_namespace_aclmask_ext(objectid, roleid, mask, how,
+                                            is_missing);
         case TypeRelationId:
-            return pg_type_aclmask(objectid, roleid, mask, how);
+            return pg_type_aclmask_ext(objectid, roleid, mask, how,
+                                       is_missing);
     }

     /* Even more special cases */
@@ -3113,16 +3132,26 @@ object_aclmask(Oid classid, Oid objectid, Oid roleid,
         return mask;

     /*
-     * Get the objects's ACL from its catalog
+     * Get the object's ACL from its catalog
      */

     cacheid = get_object_catcache_oid(classid);

     tuple = SearchSysCache1(cacheid, ObjectIdGetDatum(objectid));
     if (!HeapTupleIsValid(tuple))
-        ereport(ERROR,
-                (errcode(ERRCODE_UNDEFINED_DATABASE),
-                 errmsg("%s with OID %u does not exist", get_object_class_descr(classid), objectid)));
+    {
+        if (is_missing != NULL)
+        {
+            /* return "no privileges" instead of throwing an error */
+            *is_missing = true;
+            return 0;
+        }
+        else
+            ereport(ERROR,
+                    (errcode(ERRCODE_UNDEFINED_OBJECT),
+                     errmsg("%s with OID %u does not exist",
+                            get_object_class_descr(classid), objectid)));
+    }

     ownerId = DatumGetObjectId(SysCacheGetAttrNotNull(cacheid,
                                                       tuple,
@@ -3609,11 +3638,12 @@ pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
 }

 /*
- * Routine for examining a user's privileges for a namespace
+ * Routine for examining a user's privileges for a namespace, with is_missing
  */
 static AclMode
-pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
-                     AclMode mask, AclMaskHow how)
+pg_namespace_aclmask_ext(Oid nsp_oid, Oid roleid,
+                         AclMode mask, AclMaskHow how,
+                         bool *is_missing)
 {
     AclMode        result;
     HeapTuple    tuple;
@@ -3647,8 +3677,8 @@ pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
      */
     if (isTempNamespace(nsp_oid))
     {
-        if (object_aclcheck(DatabaseRelationId, MyDatabaseId, roleid,
-                            ACL_CREATE_TEMP) == ACLCHECK_OK)
+        if (object_aclcheck_ext(DatabaseRelationId, MyDatabaseId, roleid,
+                                ACL_CREATE_TEMP, is_missing) == ACLCHECK_OK)
             return mask & ACL_ALL_RIGHTS_SCHEMA;
         else
             return mask & ACL_USAGE;
@@ -3659,9 +3689,18 @@ pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
      */
     tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(nsp_oid));
     if (!HeapTupleIsValid(tuple))
-        ereport(ERROR,
-                (errcode(ERRCODE_UNDEFINED_SCHEMA),
-                 errmsg("schema with OID %u does not exist", nsp_oid)));
+    {
+        if (is_missing != NULL)
+        {
+            /* return "no privileges" instead of throwing an error */
+            *is_missing = true;
+            return 0;
+        }
+        else
+            ereport(ERROR,
+                    (errcode(ERRCODE_UNDEFINED_SCHEMA),
+                     errmsg("schema with OID %u does not exist", nsp_oid)));
+    }

     ownerId = ((Form_pg_namespace) GETSTRUCT(tuple))->nspowner;

@@ -3701,20 +3740,20 @@ pg_namespace_aclmask(Oid nsp_oid, Oid roleid,
 }

 /*
- * Routine for examining a user's privileges for a type.
+ * Routine for examining a user's privileges for a type, with is_missing
  */
 static AclMode
-pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
+pg_type_aclmask_ext(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how,
+                    bool *is_missing)
 {
     AclMode        result;
     HeapTuple    tuple;
+    Form_pg_type typeForm;
     Datum        aclDatum;
     bool        isNull;
     Acl           *acl;
     Oid            ownerId;

-    Form_pg_type typeForm;
-
     /* Bypass permission checks for superusers */
     if (superuser_arg(roleid))
         return mask;
@@ -3724,10 +3763,19 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
      */
     tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(type_oid));
     if (!HeapTupleIsValid(tuple))
-        ereport(ERROR,
-                (errcode(ERRCODE_UNDEFINED_OBJECT),
-                 errmsg("type with OID %u does not exist",
-                        type_oid)));
+    {
+        if (is_missing != NULL)
+        {
+            /* return "no privileges" instead of throwing an error */
+            *is_missing = true;
+            return 0;
+        }
+        else
+            ereport(ERROR,
+                    (errcode(ERRCODE_UNDEFINED_OBJECT),
+                     errmsg("type with OID %u does not exist",
+                            type_oid)));
+    }
     typeForm = (Form_pg_type) GETSTRUCT(tuple);

     /*
@@ -3741,9 +3789,20 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
         ReleaseSysCache(tuple);

         tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(elttype_oid));
-        /* this case is not a user-facing error, so elog not ereport */
         if (!HeapTupleIsValid(tuple))
-            elog(ERROR, "cache lookup failed for type %u", elttype_oid);
+        {
+            if (is_missing != NULL)
+            {
+                /* return "no privileges" instead of throwing an error */
+                *is_missing = true;
+                return 0;
+            }
+            else
+                ereport(ERROR,
+                        (errcode(ERRCODE_UNDEFINED_OBJECT),
+                         errmsg("type with OID %u does not exist",
+                                elttype_oid)));
+        }
         typeForm = (Form_pg_type) GETSTRUCT(tuple);
     }

@@ -3783,7 +3842,20 @@ pg_type_aclmask(Oid type_oid, Oid roleid, AclMode mask, AclMaskHow how)
 AclResult
 object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode)
 {
-    if (object_aclmask(classid, objectid, roleid, mode, ACLMASK_ANY) != 0)
+    return object_aclcheck_ext(classid, objectid, roleid, mode, NULL);
+}
+
+/*
+ * Exported generic routine for checking a user's access privileges to an object
+ * with is_missing
+ */
+AclResult
+object_aclcheck_ext(Oid classid, Oid objectid,
+                    Oid roleid, AclMode mode,
+                    bool *is_missing)
+{
+    if (object_aclmask_ext(classid, objectid, roleid, mode, ACLMASK_ANY,
+                           is_missing) != 0)
         return ACLCHECK_OK;
     else
         return ACLCHECK_NO_PRIV;
diff --git a/src/backend/utils/adt/acl.c b/src/backend/utils/adt/acl.c
index 809cb6f03f..798833011b 100644
--- a/src/backend/utils/adt/acl.c
+++ b/src/backend/utils/adt/acl.c
@@ -3010,14 +3010,17 @@ has_database_privilege_name_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = get_role_oid_or_public(NameStr(*username));
     mode = convert_database_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(DatabaseRelationId, databaseoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3036,14 +3039,17 @@ has_database_privilege_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = GetUserId();
     mode = convert_database_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(DatabaseRelationId, databaseoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3084,13 +3090,16 @@ has_database_privilege_id_id(PG_FUNCTION_ARGS)
     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     mode = convert_database_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(DATABASEOID, ObjectIdGetDatum(databaseoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(DatabaseRelationId, databaseoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(DatabaseRelationId, databaseoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3207,14 +3216,17 @@ has_foreign_data_wrapper_privilege_name_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = get_role_oid_or_public(NameStr(*username));
     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(ForeignDataWrapperRelationId, fdwid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3233,14 +3245,17 @@ has_foreign_data_wrapper_privilege_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = GetUserId();
     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(ForeignDataWrapperRelationId, fdwid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3281,13 +3296,16 @@ has_foreign_data_wrapper_privilege_id_id(PG_FUNCTION_ARGS)
     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     mode = convert_foreign_data_wrapper_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(FOREIGNDATAWRAPPEROID, ObjectIdGetDatum(fdwid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(ForeignDataWrapperRelationId, fdwid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(ForeignDataWrapperRelationId, fdwid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3398,14 +3416,17 @@ has_function_privilege_name_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = get_role_oid_or_public(NameStr(*username));
     mode = convert_function_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(ProcedureRelationId, functionoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(ProcedureRelationId, functionoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3424,14 +3445,17 @@ has_function_privilege_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = GetUserId();
     mode = convert_function_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(ProcedureRelationId, functionoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(ProcedureRelationId, functionoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3472,13 +3496,16 @@ has_function_privilege_id_id(PG_FUNCTION_ARGS)
     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     mode = convert_function_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(functionoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(ProcedureRelationId, functionoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(ProcedureRelationId, functionoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3598,14 +3625,17 @@ has_language_privilege_name_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = get_role_oid_or_public(NameStr(*username));
     mode = convert_language_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(LanguageRelationId, languageoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(LanguageRelationId, languageoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3624,14 +3654,17 @@ has_language_privilege_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = GetUserId();
     mode = convert_language_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(LanguageRelationId, languageoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(LanguageRelationId, languageoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3672,13 +3705,16 @@ has_language_privilege_id_id(PG_FUNCTION_ARGS)
     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     mode = convert_language_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(LANGOID, ObjectIdGetDatum(languageoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(LanguageRelationId, languageoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(LanguageRelationId, languageoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3789,14 +3825,17 @@ has_schema_privilege_name_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = get_role_oid_or_public(NameStr(*username));
     mode = convert_schema_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(NamespaceRelationId, schemaoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(NamespaceRelationId, schemaoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3815,14 +3854,17 @@ has_schema_privilege_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = GetUserId();
     mode = convert_schema_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(NamespaceRelationId, schemaoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(NamespaceRelationId, schemaoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3863,13 +3905,16 @@ has_schema_privilege_id_id(PG_FUNCTION_ARGS)
     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     mode = convert_schema_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(NAMESPACEOID, ObjectIdGetDatum(schemaoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(NamespaceRelationId, schemaoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(NamespaceRelationId, schemaoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -3982,14 +4027,17 @@ has_server_privilege_name_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = get_role_oid_or_public(NameStr(*username));
     mode = convert_server_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(ForeignServerRelationId, serverid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(ForeignServerRelationId, serverid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -4008,14 +4056,17 @@ has_server_privilege_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = GetUserId();
     mode = convert_server_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(ForeignServerRelationId, serverid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(ForeignServerRelationId, serverid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -4056,13 +4107,16 @@ has_server_privilege_id_id(PG_FUNCTION_ARGS)
     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     mode = convert_server_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(FOREIGNSERVEROID, ObjectIdGetDatum(serverid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(ForeignServerRelationId, serverid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(ForeignServerRelationId, serverid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -4173,14 +4227,17 @@ has_tablespace_privilege_name_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = get_role_oid_or_public(NameStr(*username));
     mode = convert_tablespace_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(TableSpaceRelationId, tablespaceoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(TableSpaceRelationId, tablespaceoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -4199,14 +4256,17 @@ has_tablespace_privilege_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = GetUserId();
     mode = convert_tablespace_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(TableSpaceRelationId, tablespaceoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(TableSpaceRelationId, tablespaceoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -4247,13 +4307,16 @@ has_tablespace_privilege_id_id(PG_FUNCTION_ARGS)
     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     mode = convert_tablespace_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(TABLESPACEOID, ObjectIdGetDatum(tablespaceoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(TableSpaceRelationId, tablespaceoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(TableSpaceRelationId, tablespaceoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -4363,14 +4426,17 @@ has_type_privilege_name_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = get_role_oid_or_public(NameStr(*username));
     mode = convert_type_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(TypeRelationId, typeoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -4389,14 +4455,17 @@ has_type_privilege_id(PG_FUNCTION_ARGS)
     Oid            roleid;
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     roleid = GetUserId();
     mode = convert_type_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(TypeRelationId, typeoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
@@ -4437,13 +4506,16 @@ has_type_privilege_id_id(PG_FUNCTION_ARGS)
     text       *priv_type_text = PG_GETARG_TEXT_PP(2);
     AclMode        mode;
     AclResult    aclresult;
+    bool        is_missing = false;

     mode = convert_type_priv_string(priv_type_text);

-    if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(typeoid)))
-        PG_RETURN_NULL();
+    aclresult = object_aclcheck_ext(TypeRelationId, typeoid,
+                                    roleid, mode,
+                                    &is_missing);

-    aclresult = object_aclcheck(TypeRelationId, typeoid, roleid, mode);
+    if (is_missing)
+        PG_RETURN_NULL();

     PG_RETURN_BOOL(aclresult == ACLCHECK_OK);
 }
diff --git a/src/include/utils/acl.h b/src/include/utils/acl.h
index 331a87d0e6..02bc4d08d6 100644
--- a/src/include/utils/acl.h
+++ b/src/include/utils/acl.h
@@ -239,8 +239,12 @@ extern void RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid);
 extern AclMode pg_class_aclmask(Oid table_oid, Oid roleid,
                                 AclMode mask, AclMaskHow how);

-/* generic function */
-extern AclResult object_aclcheck(Oid classid, Oid objectid, Oid roleid, AclMode mode);
+/* generic functions */
+extern AclResult object_aclcheck(Oid classid, Oid objectid,
+                                 Oid roleid, AclMode mode);
+extern AclResult object_aclcheck_ext(Oid classid, Oid objectid,
+                                     Oid roleid, AclMode mode,
+                                     bool *is_missing);

 /* special cases */
 extern AclResult pg_attribute_aclcheck(Oid table_oid, AttrNumber attnum,

В списке pgsql-bugs по дате отправления:

Предыдущее
От: Thomas Munro
Дата:
Сообщение: Re: BUG #18146: Rows reappearing in Tables after Auto-Vacuum Failure in PostgreSQL on Windows
Следующее
От: PG Bug reporting form
Дата:
Сообщение: BUG #18155: Logical Apply Worker Timeout During TableSync Causes Either Stuckness or Data Loss