Harden xxx_is_visible() functions against concurrent object drops.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 14 Oct 2023 20:13:11 +0000 (16:13 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 14 Oct 2023 20:13:11 +0000 (16:13 -0400)
For the same reasons given in commit 403ac226d, adjust these
functions to not assume that checking SearchSysCacheExists can
guarantee success of a later fetch.

This follows the same internal API choices made in the earlier commit:
add a function XXXExt(oid, is_missing) and use that to eliminate
the need for a separate existence check.  The changes are very
straightforward, though tedious.  For the moment I just made the new
functions static in namespace.c, but we could export them if a need
emerges.

Per bug #18014 from Alexander Lakhin.  Given the lack of hard evidence
that there's a bug in non-debug builds, I'm content to fix this only
in HEAD.

Discussion: https://postgr.es/m/18014-28c81cb79d44295d@postgresql.org

src/backend/catalog/namespace.c
src/include/catalog/namespace.h

index 4ceae038ec555e343911205bbc2d79e8812d494c..ff1bfae1a3c4a7e274c2abfa2a71e3ad8caba6c1 100644 (file)
@@ -183,6 +183,19 @@ char          *namespace_search_path = NULL;
 
 
 /* Local functions */
+static bool RelationIsVisibleExt(Oid relid, bool *is_missing);
+static bool TypeIsVisibleExt(Oid typid, bool *is_missing);
+static bool FunctionIsVisibleExt(Oid funcid, bool *is_missing);
+static bool OperatorIsVisibleExt(Oid oprid, bool *is_missing);
+static bool OpclassIsVisibleExt(Oid opcid, bool *is_missing);
+static bool OpfamilyIsVisibleExt(Oid opfid, bool *is_missing);
+static bool CollationIsVisibleExt(Oid collid, bool *is_missing);
+static bool ConversionIsVisibleExt(Oid conid, bool *is_missing);
+static bool StatisticsObjIsVisibleExt(Oid stxid, bool *is_missing);
+static bool TSParserIsVisibleExt(Oid prsId, bool *is_missing);
+static bool TSDictionaryIsVisibleExt(Oid dictId, bool *is_missing);
+static bool TSTemplateIsVisibleExt(Oid tmplId, bool *is_missing);
+static bool TSConfigIsVisibleExt(Oid cfgid, bool *is_missing);
 static void recomputeNamespacePath(void);
 static void AccessTempTableNamespace(bool force);
 static void InitTempTableNamespace(void);
@@ -691,6 +704,18 @@ RelnameGetRelid(const char *relname)
  */
 bool
 RelationIsVisible(Oid relid)
+{
+       return RelationIsVisibleExt(relid, NULL);
+}
+
+/*
+ * RelationIsVisibleExt
+ *             As above, but if the relation isn't found and is_missing is not NULL,
+ *             then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+RelationIsVisibleExt(Oid relid, bool *is_missing)
 {
        HeapTuple       reltup;
        Form_pg_class relform;
@@ -699,7 +724,14 @@ RelationIsVisible(Oid relid)
 
        reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
        if (!HeapTupleIsValid(reltup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for relation %u", relid);
+       }
        relform = (Form_pg_class) GETSTRUCT(reltup);
 
        recomputeNamespacePath();
@@ -799,6 +831,18 @@ TypenameGetTypidExtended(const char *typname, bool temp_ok)
  */
 bool
 TypeIsVisible(Oid typid)
+{
+       return TypeIsVisibleExt(typid, NULL);
+}
+
+/*
+ * TypeIsVisibleExt
+ *             As above, but if the type isn't found and is_missing is not NULL,
+ *             then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+TypeIsVisibleExt(Oid typid, bool *is_missing)
 {
        HeapTuple       typtup;
        Form_pg_type typform;
@@ -807,7 +851,14 @@ TypeIsVisible(Oid typid)
 
        typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
        if (!HeapTupleIsValid(typtup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for type %u", typid);
+       }
        typform = (Form_pg_type) GETSTRUCT(typtup);
 
        recomputeNamespacePath();
@@ -1436,6 +1487,18 @@ MatchNamedCall(HeapTuple proctup, int nargs, List *argnames,
  */
 bool
 FunctionIsVisible(Oid funcid)
+{
+       return FunctionIsVisibleExt(funcid, NULL);
+}
+
+/*
+ * FunctionIsVisibleExt
+ *             As above, but if the function isn't found and is_missing is not NULL,
+ *             then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+FunctionIsVisibleExt(Oid funcid, bool *is_missing)
 {
        HeapTuple       proctup;
        Form_pg_proc procform;
@@ -1444,7 +1507,14 @@ FunctionIsVisible(Oid funcid)
 
        proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(proctup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for function %u", funcid);
+       }
        procform = (Form_pg_proc) GETSTRUCT(proctup);
 
        recomputeNamespacePath();
@@ -1770,6 +1840,18 @@ OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
  */
 bool
 OperatorIsVisible(Oid oprid)
+{
+       return OperatorIsVisibleExt(oprid, NULL);
+}
+
+/*
+ * OperatorIsVisibleExt
+ *             As above, but if the operator isn't found and is_missing is not NULL,
+ *             then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+OperatorIsVisibleExt(Oid oprid, bool *is_missing)
 {
        HeapTuple       oprtup;
        Form_pg_operator oprform;
@@ -1778,7 +1860,14 @@ OperatorIsVisible(Oid oprid)
 
        oprtup = SearchSysCache1(OPEROID, ObjectIdGetDatum(oprid));
        if (!HeapTupleIsValid(oprtup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for operator %u", oprid);
+       }
        oprform = (Form_pg_operator) GETSTRUCT(oprtup);
 
        recomputeNamespacePath();
@@ -1856,6 +1945,18 @@ OpclassnameGetOpcid(Oid amid, const char *opcname)
  */
 bool
 OpclassIsVisible(Oid opcid)
+{
+       return OpclassIsVisibleExt(opcid, NULL);
+}
+
+/*
+ * OpclassIsVisibleExt
+ *             As above, but if the opclass isn't found and is_missing is not NULL,
+ *             then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+OpclassIsVisibleExt(Oid opcid, bool *is_missing)
 {
        HeapTuple       opctup;
        Form_pg_opclass opcform;
@@ -1864,7 +1965,14 @@ OpclassIsVisible(Oid opcid)
 
        opctup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opcid));
        if (!HeapTupleIsValid(opctup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for opclass %u", opcid);
+       }
        opcform = (Form_pg_opclass) GETSTRUCT(opctup);
 
        recomputeNamespacePath();
@@ -1939,6 +2047,18 @@ OpfamilynameGetOpfid(Oid amid, const char *opfname)
  */
 bool
 OpfamilyIsVisible(Oid opfid)
+{
+       return OpfamilyIsVisibleExt(opfid, NULL);
+}
+
+/*
+ * OpfamilyIsVisibleExt
+ *             As above, but if the opfamily isn't found and is_missing is not NULL,
+ *             then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+OpfamilyIsVisibleExt(Oid opfid, bool *is_missing)
 {
        HeapTuple       opftup;
        Form_pg_opfamily opfform;
@@ -1947,7 +2067,14 @@ OpfamilyIsVisible(Oid opfid)
 
        opftup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfid));
        if (!HeapTupleIsValid(opftup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for opfamily %u", opfid);
+       }
        opfform = (Form_pg_opfamily) GETSTRUCT(opftup);
 
        recomputeNamespacePath();
@@ -2071,6 +2198,18 @@ CollationGetCollid(const char *collname)
  */
 bool
 CollationIsVisible(Oid collid)
+{
+       return CollationIsVisibleExt(collid, NULL);
+}
+
+/*
+ * CollationIsVisibleExt
+ *             As above, but if the collation isn't found and is_missing is not NULL,
+ *             then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+CollationIsVisibleExt(Oid collid, bool *is_missing)
 {
        HeapTuple       colltup;
        Form_pg_collation collform;
@@ -2079,7 +2218,14 @@ CollationIsVisible(Oid collid)
 
        colltup = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
        if (!HeapTupleIsValid(colltup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for collation %u", collid);
+       }
        collform = (Form_pg_collation) GETSTRUCT(colltup);
 
        recomputeNamespacePath();
@@ -2154,6 +2300,18 @@ ConversionGetConid(const char *conname)
  */
 bool
 ConversionIsVisible(Oid conid)
+{
+       return ConversionIsVisibleExt(conid, NULL);
+}
+
+/*
+ * ConversionIsVisibleExt
+ *             As above, but if the conversion isn't found and is_missing is not NULL,
+ *             then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+ConversionIsVisibleExt(Oid conid, bool *is_missing)
 {
        HeapTuple       contup;
        Form_pg_conversion conform;
@@ -2162,7 +2320,14 @@ ConversionIsVisible(Oid conid)
 
        contup = SearchSysCache1(CONVOID, ObjectIdGetDatum(conid));
        if (!HeapTupleIsValid(contup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for conversion %u", conid);
+       }
        conform = (Form_pg_conversion) GETSTRUCT(contup);
 
        recomputeNamespacePath();
@@ -2257,16 +2422,35 @@ get_statistics_object_oid(List *names, bool missing_ok)
  *             for the unqualified statistics object name".
  */
 bool
-StatisticsObjIsVisible(Oid relid)
+StatisticsObjIsVisible(Oid stxid)
+{
+       return StatisticsObjIsVisibleExt(stxid, NULL);
+}
+
+/*
+ * StatisticsObjIsVisibleExt
+ *             As above, but if the statistics object isn't found and is_missing is
+ *             not NULL, then set *is_missing = true and return false instead of
+ *             throwing an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+StatisticsObjIsVisibleExt(Oid stxid, bool *is_missing)
 {
        HeapTuple       stxtup;
        Form_pg_statistic_ext stxform;
        Oid                     stxnamespace;
        bool            visible;
 
-       stxtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(relid));
+       stxtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(stxid));
        if (!HeapTupleIsValid(stxtup))
-               elog(ERROR, "cache lookup failed for statistics object %u", relid);
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
+               elog(ERROR, "cache lookup failed for statistics object %u", stxid);
+       }
        stxform = (Form_pg_statistic_ext) GETSTRUCT(stxtup);
 
        recomputeNamespacePath();
@@ -2381,6 +2565,18 @@ get_ts_parser_oid(List *names, bool missing_ok)
  */
 bool
 TSParserIsVisible(Oid prsId)
+{
+       return TSParserIsVisibleExt(prsId, NULL);
+}
+
+/*
+ * TSParserIsVisibleExt
+ *             As above, but if the parser isn't found and is_missing is not NULL,
+ *             then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+TSParserIsVisibleExt(Oid prsId, bool *is_missing)
 {
        HeapTuple       tup;
        Form_pg_ts_parser form;
@@ -2389,7 +2585,14 @@ TSParserIsVisible(Oid prsId)
 
        tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
        if (!HeapTupleIsValid(tup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for text search parser %u", prsId);
+       }
        form = (Form_pg_ts_parser) GETSTRUCT(tup);
 
        recomputeNamespacePath();
@@ -2507,6 +2710,18 @@ get_ts_dict_oid(List *names, bool missing_ok)
  */
 bool
 TSDictionaryIsVisible(Oid dictId)
+{
+       return TSDictionaryIsVisibleExt(dictId, NULL);
+}
+
+/*
+ * TSDictionaryIsVisibleExt
+ *             As above, but if the dictionary isn't found and is_missing is not NULL,
+ *             then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+TSDictionaryIsVisibleExt(Oid dictId, bool *is_missing)
 {
        HeapTuple       tup;
        Form_pg_ts_dict form;
@@ -2515,8 +2730,15 @@ TSDictionaryIsVisible(Oid dictId)
 
        tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
        if (!HeapTupleIsValid(tup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for text search dictionary %u",
                         dictId);
+       }
        form = (Form_pg_ts_dict) GETSTRUCT(tup);
 
        recomputeNamespacePath();
@@ -2634,6 +2856,18 @@ get_ts_template_oid(List *names, bool missing_ok)
  */
 bool
 TSTemplateIsVisible(Oid tmplId)
+{
+       return TSTemplateIsVisibleExt(tmplId, NULL);
+}
+
+/*
+ * TSTemplateIsVisibleExt
+ *             As above, but if the template isn't found and is_missing is not NULL,
+ *             then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+TSTemplateIsVisibleExt(Oid tmplId, bool *is_missing)
 {
        HeapTuple       tup;
        Form_pg_ts_template form;
@@ -2642,7 +2876,14 @@ TSTemplateIsVisible(Oid tmplId)
 
        tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
        if (!HeapTupleIsValid(tup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for text search template %u", tmplId);
+       }
        form = (Form_pg_ts_template) GETSTRUCT(tup);
 
        recomputeNamespacePath();
@@ -2760,6 +3001,18 @@ get_ts_config_oid(List *names, bool missing_ok)
  */
 bool
 TSConfigIsVisible(Oid cfgid)
+{
+       return TSConfigIsVisibleExt(cfgid, NULL);
+}
+
+/*
+ * TSConfigIsVisibleExt
+ *             As above, but if the configuration isn't found and is_missing is not
+ *             NULL, then set *is_missing = true and return false instead of throwing
+ *             an error.  (Caller must initialize *is_missing = false.)
+ */
+static bool
+TSConfigIsVisibleExt(Oid cfgid, bool *is_missing)
 {
        HeapTuple       tup;
        Form_pg_ts_config form;
@@ -2768,8 +3021,15 @@ TSConfigIsVisible(Oid cfgid)
 
        tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgid));
        if (!HeapTupleIsValid(tup))
+       {
+               if (is_missing != NULL)
+               {
+                       *is_missing = true;
+                       return false;
+               }
                elog(ERROR, "cache lookup failed for text search configuration %u",
                         cfgid);
+       }
        form = (Form_pg_ts_config) GETSTRUCT(tup);
 
        recomputeNamespacePath();
@@ -4283,152 +4543,189 @@ fetch_search_path_array(Oid *sarray, int sarray_len)
  * condition errors when a query that's scanning a catalog using an MVCC
  * snapshot uses one of these functions.  The underlying IsVisible functions
  * always use an up-to-date snapshot and so might see the object as already
- * gone when it's still visible to the transaction snapshot.  (There is no race
- * condition in the current coding because we don't accept sinval messages
- * between the SearchSysCacheExists test and the subsequent lookup.)
+ * gone when it's still visible to the transaction snapshot.
  */
 
 Datum
 pg_table_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = RelationIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(RelationIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_type_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = TypeIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(TypeIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_function_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = FunctionIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(FunctionIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_operator_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(OPEROID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = OperatorIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(OperatorIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_opclass_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(CLAOID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = OpclassIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(OpclassIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_opfamily_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(OPFAMILYOID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = OpfamilyIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(OpfamilyIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_collation_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(COLLOID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = CollationIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(CollationIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_conversion_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(CONVOID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = ConversionIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(ConversionIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_statistics_obj_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(STATEXTOID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = StatisticsObjIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(StatisticsObjIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_ts_parser_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(TSPARSEROID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = TSParserIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(TSParserIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_ts_dict_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(TSDICTOID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = TSDictionaryIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(TSDictionaryIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_ts_template_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(TSTEMPLATEOID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = TSTemplateIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(TSTemplateIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
 pg_ts_config_is_visible(PG_FUNCTION_ARGS)
 {
        Oid                     oid = PG_GETARG_OID(0);
+       bool            result;
+       bool            is_missing = false;
 
-       if (!SearchSysCacheExists1(TSCONFIGOID, ObjectIdGetDatum(oid)))
-               PG_RETURN_NULL();
+       result = TSConfigIsVisibleExt(oid, &is_missing);
 
-       PG_RETURN_BOOL(TSConfigIsVisible(oid));
+       if (is_missing)
+               PG_RETURN_NULL();
+       PG_RETURN_BOOL(result);
 }
 
 Datum
index e0279404305fb06349552a25335094f5f36a59e1..94b0d2df3c4044c8723f6031bafc919e5c433f31 100644 (file)
@@ -122,7 +122,7 @@ extern Oid  ConversionGetConid(const char *conname);
 extern bool ConversionIsVisible(Oid conid);
 
 extern Oid     get_statistics_object_oid(List *names, bool missing_ok);
-extern bool StatisticsObjIsVisible(Oid relid);
+extern bool StatisticsObjIsVisible(Oid stxid);
 
 extern Oid     get_ts_parser_oid(List *names, bool missing_ok);
 extern bool TSParserIsVisible(Oid prsId);