some namespace.c refactoring
Here are two patches that refactor the mostly repetitive "${object} is
visible" and get_${object}_oid() functions in namespace.c. This uses
the functions in objectaddress.c to look up the appropriate per-catalog
system caches and attribute numbers, similar to other refactoring
patches I have posted recently.
In both cases, there are some functions that have special behaviors that
are not easy to unify, so I left those alone for now.
Notes on 0001-Refactor-is-visible-functions.patch:
Among the functions that are being unified, some check temp schemas and
some skip them. I suppose that this is because some (most) object types
cannot normally be in temp schemas, but this isn't made explicit in the
code. I added a code comment about this, the way I understand it.
That said, you can create objects explicitly in temp schemas, so I'm not
sure the existing code is completely correct.
Notes on 0002-Refactor-common-parts-of-get_-_oid-functions.patch:
Here, I only extracted the common parts of each function but left the
actual functions alone, because they each have to produce their own
error message. There is a possibility to generalize this further,
perhaps in the style of does_not_exist_skipping(), but that looked like
a separate step to me.
Attachments:
0001-Refactor-is-visible-functions.patchtext/plain; charset=UTF-8; name=0001-Refactor-is-visible-functions.patchDownload
From dbbba36ba506c44baa90feabf3857ced3de87c78 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 14 Feb 2023 12:16:36 +0100
Subject: [PATCH 1/2] Refactor "is visible" functions
---
contrib/dblink/dblink.c | 2 +-
src/backend/catalog/namespace.c | 545 ++--------------------------
src/backend/catalog/objectaddress.c | 14 +-
src/backend/utils/adt/format_type.c | 2 +-
src/backend/utils/adt/regproc.c | 6 +-
src/backend/utils/adt/ruleutils.c | 2 +-
src/include/catalog/namespace.h | 12 +-
7 files changed, 50 insertions(+), 533 deletions(-)
diff --git a/contrib/dblink/dblink.c b/contrib/dblink/dblink.c
index 78a8bcee6e..3f345ea540 100644
--- a/contrib/dblink/dblink.c
+++ b/contrib/dblink/dblink.c
@@ -2491,7 +2491,7 @@ generate_relation_name(Relation rel)
char *result;
/* Qualify the name if not visible in search path */
- if (RelationIsVisible(RelationGetRelid(rel)))
+ if (ObjectIsVisible(RelationRelationId, RelationGetRelid(rel)))
nspname = NULL;
else
nspname = get_namespace_name(rel->rd_rel->relnamespace);
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 14e57adee2..6179c3f678 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -701,71 +701,6 @@ RelnameGetRelid(const char *relname)
}
-/*
- * RelationIsVisible
- * Determine whether a relation (identified by OID) is visible in the
- * current search path. Visible means "would be found by searching
- * for the unqualified relation name".
- */
-bool
-RelationIsVisible(Oid relid)
-{
- HeapTuple reltup;
- Form_pg_class relform;
- Oid relnamespace;
- bool visible;
-
- reltup = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
- if (!HeapTupleIsValid(reltup))
- elog(ERROR, "cache lookup failed for relation %u", relid);
- relform = (Form_pg_class) GETSTRUCT(reltup);
-
- recomputeNamespacePath();
-
- /*
- * Quick check: if it ain't in the path at all, it ain't visible. Items in
- * the system namespace are surely in the path and so we needn't even do
- * list_member_oid() for them.
- */
- relnamespace = relform->relnamespace;
- if (relnamespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(activeSearchPath, relnamespace))
- visible = false;
- else
- {
- /*
- * If it is in the path, it might still not be visible; it could be
- * hidden by another relation of the same name earlier in the path. So
- * we must do a slow check for conflicting relations.
- */
- char *relname = NameStr(relform->relname);
- ListCell *l;
-
- visible = false;
- foreach(l, activeSearchPath)
- {
- Oid namespaceId = lfirst_oid(l);
-
- if (namespaceId == relnamespace)
- {
- /* Found it first in path */
- visible = true;
- break;
- }
- if (OidIsValid(get_relname_relid(relname, namespaceId)))
- {
- /* Found something else first in path */
- break;
- }
- }
- }
-
- ReleaseSysCache(reltup);
-
- return visible;
-}
-
-
/*
* TypenameGetTypid
* Wrapper for binary compatibility.
@@ -809,72 +744,6 @@ TypenameGetTypidExtended(const char *typname, bool temp_ok)
return InvalidOid;
}
-/*
- * TypeIsVisible
- * Determine whether a type (identified by OID) is visible in the
- * current search path. Visible means "would be found by searching
- * for the unqualified type name".
- */
-bool
-TypeIsVisible(Oid typid)
-{
- HeapTuple typtup;
- Form_pg_type typform;
- Oid typnamespace;
- bool visible;
-
- typtup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
- if (!HeapTupleIsValid(typtup))
- elog(ERROR, "cache lookup failed for type %u", typid);
- typform = (Form_pg_type) GETSTRUCT(typtup);
-
- recomputeNamespacePath();
-
- /*
- * Quick check: if it ain't in the path at all, it ain't visible. Items in
- * the system namespace are surely in the path and so we needn't even do
- * list_member_oid() for them.
- */
- typnamespace = typform->typnamespace;
- if (typnamespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(activeSearchPath, typnamespace))
- visible = false;
- else
- {
- /*
- * If it is in the path, it might still not be visible; it could be
- * hidden by another type of the same name earlier in the path. So we
- * must do a slow check for conflicting types.
- */
- char *typname = NameStr(typform->typname);
- ListCell *l;
-
- visible = false;
- foreach(l, activeSearchPath)
- {
- Oid namespaceId = lfirst_oid(l);
-
- if (namespaceId == typnamespace)
- {
- /* Found it first in path */
- visible = true;
- break;
- }
- if (SearchSysCacheExists2(TYPENAMENSP,
- PointerGetDatum(typname),
- ObjectIdGetDatum(namespaceId)))
- {
- /* Found something else first in path */
- break;
- }
- }
- }
-
- ReleaseSysCache(typtup);
-
- return visible;
-}
-
/*
* FuncnameGetCandidates
@@ -2130,88 +1999,6 @@ CollationIsVisible(Oid collid)
return visible;
}
-
-/*
- * ConversionGetConid
- * Try to resolve an unqualified conversion name.
- * Returns OID if conversion found in search path, else InvalidOid.
- *
- * This is essentially the same as RelnameGetRelid.
- */
-Oid
-ConversionGetConid(const char *conname)
-{
- Oid conid;
- ListCell *l;
-
- recomputeNamespacePath();
-
- foreach(l, activeSearchPath)
- {
- Oid namespaceId = lfirst_oid(l);
-
- if (namespaceId == myTempNamespace)
- continue; /* do not look in temp namespace */
-
- conid = GetSysCacheOid2(CONNAMENSP, Anum_pg_conversion_oid,
- PointerGetDatum(conname),
- ObjectIdGetDatum(namespaceId));
- if (OidIsValid(conid))
- return conid;
- }
-
- /* Not found in path */
- return InvalidOid;
-}
-
-/*
- * ConversionIsVisible
- * Determine whether a conversion (identified by OID) is visible in the
- * current search path. Visible means "would be found by searching
- * for the unqualified conversion name".
- */
-bool
-ConversionIsVisible(Oid conid)
-{
- HeapTuple contup;
- Form_pg_conversion conform;
- Oid connamespace;
- bool visible;
-
- contup = SearchSysCache1(CONVOID, ObjectIdGetDatum(conid));
- if (!HeapTupleIsValid(contup))
- elog(ERROR, "cache lookup failed for conversion %u", conid);
- conform = (Form_pg_conversion) GETSTRUCT(contup);
-
- recomputeNamespacePath();
-
- /*
- * Quick check: if it ain't in the path at all, it ain't visible. Items in
- * the system namespace are surely in the path and so we needn't even do
- * list_member_oid() for them.
- */
- connamespace = conform->connamespace;
- if (connamespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(activeSearchPath, connamespace))
- visible = false;
- else
- {
- /*
- * If it is in the path, it might still not be visible; it could be
- * hidden by another conversion of the same name earlier in the path.
- * So we must do a slow check to see if this conversion would be found
- * by ConversionGetConid.
- */
- char *conname = NameStr(conform->conname);
-
- visible = (ConversionGetConid(conname) == conid);
- }
-
- ReleaseSysCache(contup);
-
- return visible;
-}
-
/*
* get_statistics_object_oid - find a statistics object by possibly qualified name
*
@@ -2268,72 +2055,6 @@ get_statistics_object_oid(List *names, bool missing_ok)
return stats_oid;
}
-/*
- * StatisticsObjIsVisible
- * Determine whether a statistics object (identified by OID) is visible in
- * the current search path. Visible means "would be found by searching
- * for the unqualified statistics object name".
- */
-bool
-StatisticsObjIsVisible(Oid relid)
-{
- HeapTuple stxtup;
- Form_pg_statistic_ext stxform;
- Oid stxnamespace;
- bool visible;
-
- stxtup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(relid));
- if (!HeapTupleIsValid(stxtup))
- elog(ERROR, "cache lookup failed for statistics object %u", relid);
- stxform = (Form_pg_statistic_ext) GETSTRUCT(stxtup);
-
- recomputeNamespacePath();
-
- /*
- * Quick check: if it ain't in the path at all, it ain't visible. Items in
- * the system namespace are surely in the path and so we needn't even do
- * list_member_oid() for them.
- */
- stxnamespace = stxform->stxnamespace;
- if (stxnamespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(activeSearchPath, stxnamespace))
- visible = false;
- else
- {
- /*
- * If it is in the path, it might still not be visible; it could be
- * hidden by another statistics object of the same name earlier in the
- * path. So we must do a slow check for conflicting objects.
- */
- char *stxname = NameStr(stxform->stxname);
- ListCell *l;
-
- visible = false;
- foreach(l, activeSearchPath)
- {
- Oid namespaceId = lfirst_oid(l);
-
- if (namespaceId == stxnamespace)
- {
- /* Found it first in path */
- visible = true;
- break;
- }
- if (SearchSysCacheExists2(STATEXTNAMENSP,
- PointerGetDatum(stxname),
- ObjectIdGetDatum(namespaceId)))
- {
- /* Found something else first in path */
- break;
- }
- }
- }
-
- ReleaseSysCache(stxtup);
-
- return visible;
-}
-
/*
* get_ts_parser_oid - find a TS parser by possibly qualified name
*
@@ -2391,75 +2112,6 @@ get_ts_parser_oid(List *names, bool missing_ok)
return prsoid;
}
-/*
- * TSParserIsVisible
- * Determine whether a parser (identified by OID) is visible in the
- * current search path. Visible means "would be found by searching
- * for the unqualified parser name".
- */
-bool
-TSParserIsVisible(Oid prsId)
-{
- HeapTuple tup;
- Form_pg_ts_parser form;
- Oid namespace;
- bool visible;
-
- tup = SearchSysCache1(TSPARSEROID, ObjectIdGetDatum(prsId));
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for text search parser %u", prsId);
- form = (Form_pg_ts_parser) GETSTRUCT(tup);
-
- recomputeNamespacePath();
-
- /*
- * Quick check: if it ain't in the path at all, it ain't visible. Items in
- * the system namespace are surely in the path and so we needn't even do
- * list_member_oid() for them.
- */
- namespace = form->prsnamespace;
- if (namespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(activeSearchPath, namespace))
- visible = false;
- else
- {
- /*
- * If it is in the path, it might still not be visible; it could be
- * hidden by another parser of the same name earlier in the path. So
- * we must do a slow check for conflicting parsers.
- */
- char *name = NameStr(form->prsname);
- ListCell *l;
-
- visible = false;
- foreach(l, activeSearchPath)
- {
- Oid namespaceId = lfirst_oid(l);
-
- if (namespaceId == myTempNamespace)
- continue; /* do not look in temp namespace */
-
- if (namespaceId == namespace)
- {
- /* Found it first in path */
- visible = true;
- break;
- }
- if (SearchSysCacheExists2(TSPARSERNAMENSP,
- PointerGetDatum(name),
- ObjectIdGetDatum(namespaceId)))
- {
- /* Found something else first in path */
- break;
- }
- }
- }
-
- ReleaseSysCache(tup);
-
- return visible;
-}
-
/*
* get_ts_dict_oid - find a TS dictionary by possibly qualified name
*
@@ -2517,76 +2169,6 @@ get_ts_dict_oid(List *names, bool missing_ok)
return dictoid;
}
-/*
- * TSDictionaryIsVisible
- * Determine whether a dictionary (identified by OID) is visible in the
- * current search path. Visible means "would be found by searching
- * for the unqualified dictionary name".
- */
-bool
-TSDictionaryIsVisible(Oid dictId)
-{
- HeapTuple tup;
- Form_pg_ts_dict form;
- Oid namespace;
- bool visible;
-
- tup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictId));
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for text search dictionary %u",
- dictId);
- form = (Form_pg_ts_dict) GETSTRUCT(tup);
-
- recomputeNamespacePath();
-
- /*
- * Quick check: if it ain't in the path at all, it ain't visible. Items in
- * the system namespace are surely in the path and so we needn't even do
- * list_member_oid() for them.
- */
- namespace = form->dictnamespace;
- if (namespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(activeSearchPath, namespace))
- visible = false;
- else
- {
- /*
- * If it is in the path, it might still not be visible; it could be
- * hidden by another dictionary of the same name earlier in the path.
- * So we must do a slow check for conflicting dictionaries.
- */
- char *name = NameStr(form->dictname);
- ListCell *l;
-
- visible = false;
- foreach(l, activeSearchPath)
- {
- Oid namespaceId = lfirst_oid(l);
-
- if (namespaceId == myTempNamespace)
- continue; /* do not look in temp namespace */
-
- if (namespaceId == namespace)
- {
- /* Found it first in path */
- visible = true;
- break;
- }
- if (SearchSysCacheExists2(TSDICTNAMENSP,
- PointerGetDatum(name),
- ObjectIdGetDatum(namespaceId)))
- {
- /* Found something else first in path */
- break;
- }
- }
- }
-
- ReleaseSysCache(tup);
-
- return visible;
-}
-
/*
* get_ts_template_oid - find a TS template by possibly qualified name
*
@@ -2644,75 +2226,6 @@ get_ts_template_oid(List *names, bool missing_ok)
return tmploid;
}
-/*
- * TSTemplateIsVisible
- * Determine whether a template (identified by OID) is visible in the
- * current search path. Visible means "would be found by searching
- * for the unqualified template name".
- */
-bool
-TSTemplateIsVisible(Oid tmplId)
-{
- HeapTuple tup;
- Form_pg_ts_template form;
- Oid namespace;
- bool visible;
-
- tup = SearchSysCache1(TSTEMPLATEOID, ObjectIdGetDatum(tmplId));
- if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for text search template %u", tmplId);
- form = (Form_pg_ts_template) GETSTRUCT(tup);
-
- recomputeNamespacePath();
-
- /*
- * Quick check: if it ain't in the path at all, it ain't visible. Items in
- * the system namespace are surely in the path and so we needn't even do
- * list_member_oid() for them.
- */
- namespace = form->tmplnamespace;
- if (namespace != PG_CATALOG_NAMESPACE &&
- !list_member_oid(activeSearchPath, namespace))
- visible = false;
- else
- {
- /*
- * If it is in the path, it might still not be visible; it could be
- * hidden by another template of the same name earlier in the path. So
- * we must do a slow check for conflicting templates.
- */
- char *name = NameStr(form->tmplname);
- ListCell *l;
-
- visible = false;
- foreach(l, activeSearchPath)
- {
- Oid namespaceId = lfirst_oid(l);
-
- if (namespaceId == myTempNamespace)
- continue; /* do not look in temp namespace */
-
- if (namespaceId == namespace)
- {
- /* Found it first in path */
- visible = true;
- break;
- }
- if (SearchSysCacheExists2(TSTEMPLATENAMENSP,
- PointerGetDatum(name),
- ObjectIdGetDatum(namespaceId)))
- {
- /* Found something else first in path */
- break;
- }
- }
- }
-
- ReleaseSysCache(tup);
-
- return visible;
-}
-
/*
* get_ts_config_oid - find a TS config by possibly qualified name
*
@@ -2770,25 +2283,25 @@ get_ts_config_oid(List *names, bool missing_ok)
return cfgoid;
}
+
/*
- * TSConfigIsVisible
- * Determine whether a text search configuration (identified by OID)
+ * ObjectIsVisible
+ * Determine whether an object (identified by class ID and OID)
* is visible in the current search path. Visible means "would be found
- * by searching for the unqualified text search configuration name".
+ * by searching for the unqualified object name".
*/
bool
-TSConfigIsVisible(Oid cfgid)
+ObjectIsVisible(Oid classid, Oid objectid)
{
+ int oid_cacheid = get_object_catcache_oid(classid);
HeapTuple tup;
- Form_pg_ts_config form;
+ bool isnull;
Oid namespace;
bool visible;
- tup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgid));
+ tup = SearchSysCache1(oid_cacheid, ObjectIdGetDatum(objectid));
if (!HeapTupleIsValid(tup))
- elog(ERROR, "cache lookup failed for text search configuration %u",
- cfgid);
- form = (Form_pg_ts_config) GETSTRUCT(tup);
+ elog(ERROR, "cache lookup failed for %s %u", get_object_class_descr(classid), objectid);
recomputeNamespacePath();
@@ -2797,7 +2310,11 @@ TSConfigIsVisible(Oid cfgid)
* the system namespace are surely in the path and so we needn't even do
* list_member_oid() for them.
*/
- namespace = form->cfgnamespace;
+ namespace = DatumGetObjectId(SysCacheGetAttr(oid_cacheid,
+ tup,
+ get_object_attnum_namespace(classid),
+ &isnull));
+ Assert(!isnull);
if (namespace != PG_CATALOG_NAMESPACE &&
!list_member_oid(activeSearchPath, namespace))
visible = false;
@@ -2808,16 +2325,24 @@ TSConfigIsVisible(Oid cfgid)
* hidden by another configuration of the same name earlier in the
* path. So we must do a slow check for conflicting configurations.
*/
- char *name = NameStr(form->cfgname);
+ Datum name_datum;
ListCell *l;
+ name_datum = SysCacheGetAttr(oid_cacheid, tup, get_object_attnum_name(classid), &isnull);
+ Assert(!isnull);
+
visible = false;
foreach(l, activeSearchPath)
{
Oid namespaceId = lfirst_oid(l);
- if (namespaceId == myTempNamespace)
- continue; /* do not look in temp namespace */
+ /*
+ * Do not look in temp namespace for object types that don't
+ * support temporary objects
+ */
+ if (!(classid == RelationRelationId || classid == TypeRelationId) &&
+ namespaceId == myTempNamespace)
+ continue;
if (namespaceId == namespace)
{
@@ -2825,8 +2350,8 @@ TSConfigIsVisible(Oid cfgid)
visible = true;
break;
}
- if (SearchSysCacheExists2(TSCONFIGNAMENSP,
- PointerGetDatum(name),
+ if (SearchSysCacheExists2(get_object_catcache_name(classid),
+ name_datum,
ObjectIdGetDatum(namespaceId)))
{
/* Found something else first in path */
@@ -4509,7 +4034,7 @@ pg_table_is_visible(PG_FUNCTION_ARGS)
if (!SearchSysCacheExists1(RELOID, ObjectIdGetDatum(oid)))
PG_RETURN_NULL();
- PG_RETURN_BOOL(RelationIsVisible(oid));
+ PG_RETURN_BOOL(ObjectIsVisible(RelationRelationId, oid));
}
Datum
@@ -4520,7 +4045,7 @@ pg_type_is_visible(PG_FUNCTION_ARGS)
if (!SearchSysCacheExists1(TYPEOID, ObjectIdGetDatum(oid)))
PG_RETURN_NULL();
- PG_RETURN_BOOL(TypeIsVisible(oid));
+ PG_RETURN_BOOL(ObjectIsVisible(TypeRelationId, oid));
}
Datum
@@ -4586,7 +4111,7 @@ pg_conversion_is_visible(PG_FUNCTION_ARGS)
if (!SearchSysCacheExists1(CONVOID, ObjectIdGetDatum(oid)))
PG_RETURN_NULL();
- PG_RETURN_BOOL(ConversionIsVisible(oid));
+ PG_RETURN_BOOL(ObjectIsVisible(ConversionRelationId, oid));
}
Datum
@@ -4597,7 +4122,7 @@ pg_statistics_obj_is_visible(PG_FUNCTION_ARGS)
if (!SearchSysCacheExists1(STATEXTOID, ObjectIdGetDatum(oid)))
PG_RETURN_NULL();
- PG_RETURN_BOOL(StatisticsObjIsVisible(oid));
+ PG_RETURN_BOOL(ObjectIsVisible(StatisticExtRelationId, oid));
}
Datum
@@ -4608,7 +4133,7 @@ pg_ts_parser_is_visible(PG_FUNCTION_ARGS)
if (!SearchSysCacheExists1(TSPARSEROID, ObjectIdGetDatum(oid)))
PG_RETURN_NULL();
- PG_RETURN_BOOL(TSParserIsVisible(oid));
+ PG_RETURN_BOOL(ObjectIsVisible(TSParserRelationId, oid));
}
Datum
@@ -4619,7 +4144,7 @@ pg_ts_dict_is_visible(PG_FUNCTION_ARGS)
if (!SearchSysCacheExists1(TSDICTOID, ObjectIdGetDatum(oid)))
PG_RETURN_NULL();
- PG_RETURN_BOOL(TSDictionaryIsVisible(oid));
+ PG_RETURN_BOOL(ObjectIsVisible(TSDictionaryRelationId, oid));
}
Datum
@@ -4630,7 +4155,7 @@ pg_ts_template_is_visible(PG_FUNCTION_ARGS)
if (!SearchSysCacheExists1(TSTEMPLATEOID, ObjectIdGetDatum(oid)))
PG_RETURN_NULL();
- PG_RETURN_BOOL(TSTemplateIsVisible(oid));
+ PG_RETURN_BOOL(ObjectIsVisible(TSTemplateRelationId, oid));
}
Datum
@@ -4641,7 +4166,7 @@ pg_ts_config_is_visible(PG_FUNCTION_ARGS)
if (!SearchSysCacheExists1(TSCONFIGOID, ObjectIdGetDatum(oid)))
PG_RETURN_NULL();
- PG_RETURN_BOOL(TSConfigIsVisible(oid));
+ PG_RETURN_BOOL(ObjectIsVisible(TSConfigRelationId, oid));
}
Datum
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index 2f688166e1..15ba7ee316 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -3097,7 +3097,7 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
conv = (Form_pg_conversion) GETSTRUCT(conTup);
/* Qualify the name if not visible in search path */
- if (ConversionIsVisible(object->objectId))
+ if (ObjectIsVisible(ConversionRelationId, object->objectId))
nspname = NULL;
else
nspname = get_namespace_name(conv->connamespace);
@@ -3468,7 +3468,7 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
stxForm = (Form_pg_statistic_ext) GETSTRUCT(stxTup);
/* Qualify the name if not visible in search path */
- if (StatisticsObjIsVisible(object->objectId))
+ if (ObjectIsVisible(StatisticExtRelationId, object->objectId))
nspname = NULL;
else
nspname = get_namespace_name(stxForm->stxnamespace);
@@ -3499,7 +3499,7 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
prsForm = (Form_pg_ts_parser) GETSTRUCT(tup);
/* Qualify the name if not visible in search path */
- if (TSParserIsVisible(object->objectId))
+ if (ObjectIsVisible(TSParserRelationId, object->objectId))
nspname = NULL;
else
nspname = get_namespace_name(prsForm->prsnamespace);
@@ -3530,7 +3530,7 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
dictForm = (Form_pg_ts_dict) GETSTRUCT(tup);
/* Qualify the name if not visible in search path */
- if (TSDictionaryIsVisible(object->objectId))
+ if (ObjectIsVisible(TSDictionaryRelationId, object->objectId))
nspname = NULL;
else
nspname = get_namespace_name(dictForm->dictnamespace);
@@ -3561,7 +3561,7 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
tmplForm = (Form_pg_ts_template) GETSTRUCT(tup);
/* Qualify the name if not visible in search path */
- if (TSTemplateIsVisible(object->objectId))
+ if (ObjectIsVisible(TSTemplateRelationId, object->objectId))
nspname = NULL;
else
nspname = get_namespace_name(tmplForm->tmplnamespace);
@@ -3592,7 +3592,7 @@ getObjectDescription(const ObjectAddress *object, bool missing_ok)
cfgForm = (Form_pg_ts_config) GETSTRUCT(tup);
/* Qualify the name if not visible in search path */
- if (TSConfigIsVisible(object->objectId))
+ if (ObjectIsVisible(TSConfigRelationId, object->objectId))
nspname = NULL;
else
nspname = get_namespace_name(cfgForm->cfgnamespace);
@@ -4102,7 +4102,7 @@ getRelationDescription(StringInfo buffer, Oid relid, bool missing_ok)
relForm = (Form_pg_class) GETSTRUCT(relTup);
/* Qualify the name if not visible in search path */
- if (RelationIsVisible(relid))
+ if (ObjectIsVisible(RelationRelationId, relid))
nspname = NULL;
else
nspname = get_namespace_name(relForm->relnamespace);
diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c
index 12402a0637..1e880d4dec 100644
--- a/src/backend/utils/adt/format_type.c
+++ b/src/backend/utils/adt/format_type.c
@@ -308,7 +308,7 @@ format_type_extended(Oid type_oid, int32 typemod, bits16 flags)
char *typname;
if ((flags & FORMAT_TYPE_FORCE_QUALIFY) == 0 &&
- TypeIsVisible(type_oid))
+ ObjectIsVisible(TypeRelationId, type_oid))
nspname = NULL;
else
nspname = get_namespace_name_or_temp(typeform->typnamespace);
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 296930eb3b..4e65dfe10a 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -973,7 +973,7 @@ regclassout(PG_FUNCTION_ARGS)
/*
* Would this class be found by regclassin? If not, qualify it.
*/
- if (RelationIsVisible(classid))
+ if (ObjectIsVisible(RelationRelationId, classid))
nspname = NULL;
else
nspname = get_namespace_name(classform->relnamespace);
@@ -1359,7 +1359,7 @@ regconfigout(PG_FUNCTION_ARGS)
/*
* Would this config be found by regconfigin? If not, qualify it.
*/
- if (TSConfigIsVisible(cfgid))
+ if (ObjectIsVisible(TSConfigRelationId, cfgid))
nspname = NULL;
else
nspname = get_namespace_name(cfgform->cfgnamespace);
@@ -1470,7 +1470,7 @@ regdictionaryout(PG_FUNCTION_ARGS)
* Would this dictionary be found by regdictionaryin? If not, qualify
* it.
*/
- if (TSDictionaryIsVisible(dictid))
+ if (ObjectIsVisible(TSDictionaryRelationId, dictid))
nspname = NULL;
else
nspname = get_namespace_name(dictform->dictnamespace);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 9ac42efdbc..3ad0f45454 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -11689,7 +11689,7 @@ generate_relation_name(Oid relid, List *namespaces)
/* Otherwise, qualify the name if not visible in search path */
if (!need_qual)
- need_qual = !RelationIsVisible(relid);
+ need_qual = !ObjectIsVisible(RelationRelationId, relid);
if (need_qual)
nspname = get_namespace_name_or_temp(reltup->relnamespace);
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index f64a0ec26b..eb23b35e4b 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -90,11 +90,9 @@ extern Oid RangeVarGetAndCheckCreationNamespace(RangeVar *relation,
Oid *existing_relation_id);
extern void RangeVarAdjustRelationPersistence(RangeVar *newRelation, Oid nspid);
extern Oid RelnameGetRelid(const char *relname);
-extern bool RelationIsVisible(Oid relid);
extern Oid TypenameGetTypid(const char *typname);
extern Oid TypenameGetTypidExtended(const char *typname, bool temp_ok);
-extern bool TypeIsVisible(Oid typid);
extern FuncCandidateList FuncnameGetCandidates(List *names,
int nargs, List *argnames,
@@ -118,23 +116,17 @@ extern bool OpfamilyIsVisible(Oid opfid);
extern Oid CollationGetCollid(const char *collname);
extern bool CollationIsVisible(Oid collid);
-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 Oid get_ts_parser_oid(List *names, bool missing_ok);
-extern bool TSParserIsVisible(Oid prsId);
extern Oid get_ts_dict_oid(List *names, bool missing_ok);
-extern bool TSDictionaryIsVisible(Oid dictId);
extern Oid get_ts_template_oid(List *names, bool missing_ok);
-extern bool TSTemplateIsVisible(Oid tmplId);
extern Oid get_ts_config_oid(List *names, bool missing_ok);
-extern bool TSConfigIsVisible(Oid cfgid);
+
+extern bool ObjectIsVisible(Oid classid, Oid objectid);
extern void DeconstructQualifiedName(List *names,
char **nspname_p,
base-commit: 3b12e68a5c4609deb9fc69607e0aec072700c0a9
--
2.39.1
0002-Refactor-common-parts-of-get_-_oid-functions.patchtext/plain; charset=UTF-8; name=0002-Refactor-common-parts-of-get_-_oid-functions.patchDownload
From 483fada9676f5c9b48ca5d30e1708fe41928a178 Mon Sep 17 00:00:00 2001
From: Peter Eisentraut <peter@eisentraut.org>
Date: Tue, 14 Feb 2023 13:28:42 +0100
Subject: [PATCH 2/2] Refactor common parts of get_*_oid() functions
---
src/backend/catalog/namespace.c | 204 ++++++--------------------------
1 file changed, 39 insertions(+), 165 deletions(-)
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 6179c3f678..289b0b7d1a 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -2000,32 +2000,34 @@ CollationIsVisible(Oid collid)
}
/*
- * get_statistics_object_oid - find a statistics object by possibly qualified name
+ * get_object_oid_common - find an object by possibly qualified name
*
- * If not found, returns InvalidOid if missing_ok, else throws error
+ * If not found, returns InvalidOid. The argument missing_ok only applies to
+ * the namespace lookup.
*/
-Oid
-get_statistics_object_oid(List *names, bool missing_ok)
+static Oid
+get_object_oid_common(Oid classid, List *names, bool missing_ok)
{
char *schemaname;
- char *stats_name;
+ char *objectname;
Oid namespaceId;
- Oid stats_oid = InvalidOid;
+ Oid objectid = InvalidOid;
ListCell *l;
/* deconstruct the name list */
- DeconstructQualifiedName(names, &schemaname, &stats_name);
+ DeconstructQualifiedName(names, &schemaname, &objectname);
if (schemaname)
{
/* use exact schema given */
namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
if (missing_ok && !OidIsValid(namespaceId))
- stats_oid = InvalidOid;
+ objectid = InvalidOid;
else
- stats_oid = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
- PointerGetDatum(stats_name),
- ObjectIdGetDatum(namespaceId));
+ objectid = GetSysCacheOid2(get_object_catcache_name(classid),
+ get_object_attnum_oid(classid),
+ PointerGetDatum(objectname),
+ ObjectIdGetDatum(namespaceId));
}
else
{
@@ -2038,14 +2040,30 @@ get_statistics_object_oid(List *names, bool missing_ok)
if (namespaceId == myTempNamespace)
continue; /* do not look in temp namespace */
- stats_oid = GetSysCacheOid2(STATEXTNAMENSP, Anum_pg_statistic_ext_oid,
- PointerGetDatum(stats_name),
- ObjectIdGetDatum(namespaceId));
- if (OidIsValid(stats_oid))
+ objectid = GetSysCacheOid2(get_object_catcache_name(classid),
+ get_object_attnum_oid(classid),
+ PointerGetDatum(objectname),
+ ObjectIdGetDatum(namespaceId));
+ if (OidIsValid(objectid))
break;
}
}
+ return objectid;
+}
+
+/*
+ * get_statistics_object_oid - find a statistics object by possibly qualified name
+ *
+ * If not found, returns InvalidOid if missing_ok, else throws error
+ */
+Oid
+get_statistics_object_oid(List *names, bool missing_ok)
+{
+ Oid stats_oid;
+
+ stats_oid = get_object_oid_common(StatisticExtRelationId, names, missing_ok);
+
if (!OidIsValid(stats_oid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
@@ -2063,45 +2081,9 @@ get_statistics_object_oid(List *names, bool missing_ok)
Oid
get_ts_parser_oid(List *names, bool missing_ok)
{
- char *schemaname;
- char *parser_name;
- Oid namespaceId;
- Oid prsoid = InvalidOid;
- ListCell *l;
-
- /* deconstruct the name list */
- DeconstructQualifiedName(names, &schemaname, &parser_name);
+ Oid prsoid;
- if (schemaname)
- {
- /* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
- if (missing_ok && !OidIsValid(namespaceId))
- prsoid = InvalidOid;
- else
- prsoid = GetSysCacheOid2(TSPARSERNAMENSP, Anum_pg_ts_parser_oid,
- PointerGetDatum(parser_name),
- ObjectIdGetDatum(namespaceId));
- }
- else
- {
- /* search for it in search path */
- recomputeNamespacePath();
-
- foreach(l, activeSearchPath)
- {
- namespaceId = lfirst_oid(l);
-
- if (namespaceId == myTempNamespace)
- continue; /* do not look in temp namespace */
-
- prsoid = GetSysCacheOid2(TSPARSERNAMENSP, Anum_pg_ts_parser_oid,
- PointerGetDatum(parser_name),
- ObjectIdGetDatum(namespaceId));
- if (OidIsValid(prsoid))
- break;
- }
- }
+ prsoid = get_object_oid_common(TSParserRelationId, names, missing_ok);
if (!OidIsValid(prsoid) && !missing_ok)
ereport(ERROR,
@@ -2120,45 +2102,9 @@ get_ts_parser_oid(List *names, bool missing_ok)
Oid
get_ts_dict_oid(List *names, bool missing_ok)
{
- char *schemaname;
- char *dict_name;
- Oid namespaceId;
- Oid dictoid = InvalidOid;
- ListCell *l;
-
- /* deconstruct the name list */
- DeconstructQualifiedName(names, &schemaname, &dict_name);
+ Oid dictoid;
- if (schemaname)
- {
- /* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
- if (missing_ok && !OidIsValid(namespaceId))
- dictoid = InvalidOid;
- else
- dictoid = GetSysCacheOid2(TSDICTNAMENSP, Anum_pg_ts_dict_oid,
- PointerGetDatum(dict_name),
- ObjectIdGetDatum(namespaceId));
- }
- else
- {
- /* search for it in search path */
- recomputeNamespacePath();
-
- foreach(l, activeSearchPath)
- {
- namespaceId = lfirst_oid(l);
-
- if (namespaceId == myTempNamespace)
- continue; /* do not look in temp namespace */
-
- dictoid = GetSysCacheOid2(TSDICTNAMENSP, Anum_pg_ts_dict_oid,
- PointerGetDatum(dict_name),
- ObjectIdGetDatum(namespaceId));
- if (OidIsValid(dictoid))
- break;
- }
- }
+ dictoid = get_object_oid_common(TSDictionaryRelationId, names, missing_ok);
if (!OidIsValid(dictoid) && !missing_ok)
ereport(ERROR,
@@ -2177,45 +2123,9 @@ get_ts_dict_oid(List *names, bool missing_ok)
Oid
get_ts_template_oid(List *names, bool missing_ok)
{
- char *schemaname;
- char *template_name;
- Oid namespaceId;
Oid tmploid = InvalidOid;
- ListCell *l;
-
- /* deconstruct the name list */
- DeconstructQualifiedName(names, &schemaname, &template_name);
-
- if (schemaname)
- {
- /* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
- if (missing_ok && !OidIsValid(namespaceId))
- tmploid = InvalidOid;
- else
- tmploid = GetSysCacheOid2(TSTEMPLATENAMENSP, Anum_pg_ts_template_oid,
- PointerGetDatum(template_name),
- ObjectIdGetDatum(namespaceId));
- }
- else
- {
- /* search for it in search path */
- recomputeNamespacePath();
-
- foreach(l, activeSearchPath)
- {
- namespaceId = lfirst_oid(l);
-
- if (namespaceId == myTempNamespace)
- continue; /* do not look in temp namespace */
- tmploid = GetSysCacheOid2(TSTEMPLATENAMENSP, Anum_pg_ts_template_oid,
- PointerGetDatum(template_name),
- ObjectIdGetDatum(namespaceId));
- if (OidIsValid(tmploid))
- break;
- }
- }
+ tmploid = get_object_oid_common(TSTemplateRelationId, names, missing_ok);
if (!OidIsValid(tmploid) && !missing_ok)
ereport(ERROR,
@@ -2234,45 +2144,9 @@ get_ts_template_oid(List *names, bool missing_ok)
Oid
get_ts_config_oid(List *names, bool missing_ok)
{
- char *schemaname;
- char *config_name;
- Oid namespaceId;
Oid cfgoid = InvalidOid;
- ListCell *l;
-
- /* deconstruct the name list */
- DeconstructQualifiedName(names, &schemaname, &config_name);
- if (schemaname)
- {
- /* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname, missing_ok);
- if (missing_ok && !OidIsValid(namespaceId))
- cfgoid = InvalidOid;
- else
- cfgoid = GetSysCacheOid2(TSCONFIGNAMENSP, Anum_pg_ts_config_oid,
- PointerGetDatum(config_name),
- ObjectIdGetDatum(namespaceId));
- }
- else
- {
- /* search for it in search path */
- recomputeNamespacePath();
-
- foreach(l, activeSearchPath)
- {
- namespaceId = lfirst_oid(l);
-
- if (namespaceId == myTempNamespace)
- continue; /* do not look in temp namespace */
-
- cfgoid = GetSysCacheOid2(TSCONFIGNAMENSP, Anum_pg_ts_config_oid,
- PointerGetDatum(config_name),
- ObjectIdGetDatum(namespaceId));
- if (OidIsValid(cfgoid))
- break;
- }
- }
+ cfgoid = get_object_oid_common(TSConfigRelationId, names, missing_ok);
if (!OidIsValid(cfgoid) && !missing_ok)
ereport(ERROR,
--
2.39.1
Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:
Here are two patches that refactor the mostly repetitive "${object} is
visible" and get_${object}_oid() functions in namespace.c. This uses
the functions in objectaddress.c to look up the appropriate per-catalog
system caches and attribute numbers, similar to other refactoring
patches I have posted recently.
This does not look like a simple refactoring patch to me. I have
very serious concerns first about whether it even preserves the
existing semantics, and second about whether there is a performance
penalty.
regards, tom lane
On 2023-Feb-15, Tom Lane wrote:
Peter Eisentraut <peter.eisentraut@enterprisedb.com> writes:
Here are two patches that refactor the mostly repetitive "${object} is
visible" and get_${object}_oid() functions in namespace.c. This uses
the functions in objectaddress.c to look up the appropriate per-catalog
system caches and attribute numbers, similar to other refactoring
patches I have posted recently.This does not look like a simple refactoring patch to me. I have
very serious concerns first about whether it even preserves the
existing semantics, and second about whether there is a performance
penalty.
I suppose there are two possible questionable angles from a performance
POV:
1. the new code uses get_object_property_data() when previously there
was a straight dereference after casting to the right struct type. So
how expensive is that? I think the answer to that is not great, because
it does a linear array scan on ObjectProperty. Maybe we need a better
answer.
2. other accesses to the data are done using SysCacheGetAttr instead of
straight struct access dereferences. I expect that most of the fields
being accessed have attcacheoff set, which allows pretty fast access to
the field in question, so it's not *that* bad. (For cases where
attcacheoff is not set, then the original code would also have to deform
the tuple.) Still, it's going to have nontrivial impact in any
microbenchmarking.
That said, I think most of this code is invoked for DDL, where
performance is not so critical; probably just fixing
get_object_property_data to not be so naïve would suffice.
Queries are another matter. I can't think of a way to determine which
of these paths are used for queries, so that we can optimize more (IOW:
just plain not rewrite those); manually going through the callers seems
a bit laborious, but perhaps doable.
--
Álvaro Herrera Breisgau, Deutschland — https://www.EnterpriseDB.com/
Thou shalt check the array bounds of all strings (indeed, all arrays), for
surely where thou typest "foo" someone someday shall type
"supercalifragilisticexpialidocious" (5th Commandment for C programmers)
On Tue, Feb 14, 2023 at 02:32:04PM +0100, Peter Eisentraut wrote:
Notes on 0001-Refactor-is-visible-functions.patch:
Among the functions that are being unified, some check temp schemas and some
skip them. I suppose that this is because some (most) object types cannot
normally be in temp schemas, but this isn't made explicit in the code. I
added a code comment about this, the way I understand it.That said, you can create objects explicitly in temp schemas, so I'm not
sure the existing code is completely correct.
+ /* + * Do not look in temp namespace for object types that don't + * support temporary objects + */ + if (!(classid == RelationRelationId || classid == TypeRelationId) && + namespaceId == myTempNamespace) + continue;
I think the reason for the class-specific *IsVisible behavior is alignment
with the lookup rules that CVE-2007-2138 introduced (commit aa27977). "CREATE
FUNCTION pg_temp.f(...)" works, but calling the resulting function requires a
schema-qualified name regardless of search_path. Since *IsVisible functions
determine whether you can reach the object without schema qualification, their
outcomes shall reflect those CVE-2007-2138 rules.
On 15.02.23 19:04, Alvaro Herrera wrote:
That said, I think most of this code is invoked for DDL, where
performance is not so critical; probably just fixing
get_object_property_data to not be so naïve would suffice.
Ok, I'll look into that.
Queries are another matter. I can't think of a way to determine which
of these paths are used for queries, so that we can optimize more (IOW:
just plain not rewrite those); manually going through the callers seems
a bit laborious, but perhaps doable.
The "is visible" functions are only used for the likes of psql, pg_dump,
query tree reversing, object descriptions -- nothing that is in a normal
query unless you explicitly call it.
The get_*_oid() functions are used mostly for DDL to find conflicting
objects. The text-search related ones can be invoked via some user
functions, if you specify a TS object other than the default one. I
will check into what the impact of that is.
On 15.02.23 06:04, Tom Lane wrote:
I have
very serious concerns first about whether it even preserves the
existing semantics, and second about whether there is a performance
penalty.
We can work out the performance issues, but what are your concerns about
semantics?
On 20.02.23 15:03, Peter Eisentraut wrote:
On 15.02.23 19:04, Alvaro Herrera wrote:
That said, I think most of this code is invoked for DDL, where
performance is not so critical; probably just fixing
get_object_property_data to not be so naïve would suffice.Ok, I'll look into that.
I did a variety of performance testing on this now.
I wrote a C function that calls the "is visible" functions in a tight loop:
Datum
pg_test_visible(PG_FUNCTION_ARGS)
{
int32 count = PG_GETARG_INT32(0);
Oid relid = PG_GETARG_OID(1);
Oid typid = PG_GETARG_OID(2);
for (int i = 0; i < count; i++)
{
RelationIsVisible(relid);
TypeIsVisible(typid);
//ObjectIsVisible(RelationRelationId, relid);
//ObjectIsVisible(TypeRelationId, typid);
}
PG_RETURN_VOID();
}
(It's calling two different ones to defeat the caching in
get_object_property_data().)
Here are some run times:
unpatched:
select pg_test_visible(100_000_000, 'pg_class', 'int4');
Time: 4536.747 ms (00:04.537)
select pg_test_visible(100_000_000, 'tenk1', 'widget');
Time: 10828.802 ms (00:10.829)
(Note that the "is visible" functions special case system catalogs.)
patched:
select pg_test_visible(100_000_000, 'pg_class', 'int4');
Time: 11409.948 ms (00:11.410)
select pg_test_visible(100_000_000, 'tenk1', 'widget');
Time: 18649.496 ms (00:18.649)
So, it's slower, but it's not clear whether it matters in practice,
considering this test.
I also wondered if this is visible through a normal external function
call, so I tried
do $$ begin perform pg_get_statisticsobjdef(28999) from
generate_series(1, 1_000_000); end $$;
(where that is the OID of the first object from select * from
pg_statistic_ext; in the regression database).
unpatched:
Time: 6952.259 ms (00:06.952)
patched (first patch only):
Time: 6993.655 ms (00:06.994)
patched (both patches):
Time: 7114.290 ms (00:07.114)
So there is some visible impact, but again, the test isn't realistic.
Then I tried a few ways to make get_object_property_data() faster. I
tried building a class_id+index cache that is qsort'ed (once) and then
bsearch'ed, that helped only minimally, not enough to make up the
difference. I also tried just searching the class_id+index cache
linearly, hoping maybe that if the cache is smaller it would be more
efficient to access, but that actually made things (minimally) worse,
probably because of the indirection. So it might be hard to get much
more out of this. I also thought about PerfectHash, but I didn't code
that up yet.
Another way would be to not use get_object_property_data() at all but
write a "common" function that we pass in all it needs hardcodedly, like
bool
RelationIsVisible(Oid relid)
{
return IsVisible_common(RELOID,
Anum_pg_class_relname
Anum_pg_class_relnamespace);
}
This would still save a lot of duplicate code.
But again, I don't think the micro-performance really matters here.
On Thu, 23 Feb 2023 at 16:38, Peter Eisentraut
<peter.eisentraut@enterprisedb.com> wrote:
On 20.02.23 15:03, Peter Eisentraut wrote:
On 15.02.23 19:04, Alvaro Herrera wrote:
That said, I think most of this code is invoked for DDL, where
performance is not so critical; probably just fixing
get_object_property_data to not be so naïve would suffice.Ok, I'll look into that.
I did a variety of performance testing on this now.
I wrote a C function that calls the "is visible" functions in a tight loop:
Datum
pg_test_visible(PG_FUNCTION_ARGS)
{
int32 count = PG_GETARG_INT32(0);
Oid relid = PG_GETARG_OID(1);
Oid typid = PG_GETARG_OID(2);for (int i = 0; i < count; i++)
{
RelationIsVisible(relid);
TypeIsVisible(typid);
//ObjectIsVisible(RelationRelationId, relid);
//ObjectIsVisible(TypeRelationId, typid);
}PG_RETURN_VOID();
}(It's calling two different ones to defeat the caching in
get_object_property_data().)Here are some run times:
unpatched:
select pg_test_visible(100_000_000, 'pg_class', 'int4');
Time: 4536.747 ms (00:04.537)select pg_test_visible(100_000_000, 'tenk1', 'widget');
Time: 10828.802 ms (00:10.829)(Note that the "is visible" functions special case system catalogs.)
patched:
select pg_test_visible(100_000_000, 'pg_class', 'int4');
Time: 11409.948 ms (00:11.410)select pg_test_visible(100_000_000, 'tenk1', 'widget');
Time: 18649.496 ms (00:18.649)So, it's slower, but it's not clear whether it matters in practice,
considering this test.I also wondered if this is visible through a normal external function
call, so I trieddo $$ begin perform pg_get_statisticsobjdef(28999) from
generate_series(1, 1_000_000); end $$;(where that is the OID of the first object from select * from
pg_statistic_ext; in the regression database).unpatched:
Time: 6952.259 ms (00:06.952)
patched (first patch only):
Time: 6993.655 ms (00:06.994)
patched (both patches):
Time: 7114.290 ms (00:07.114)
So there is some visible impact, but again, the test isn't realistic.
Then I tried a few ways to make get_object_property_data() faster. I
tried building a class_id+index cache that is qsort'ed (once) and then
bsearch'ed, that helped only minimally, not enough to make up the
difference. I also tried just searching the class_id+index cache
linearly, hoping maybe that if the cache is smaller it would be more
efficient to access, but that actually made things (minimally) worse,
probably because of the indirection. So it might be hard to get much
more out of this. I also thought about PerfectHash, but I didn't code
that up yet.Another way would be to not use get_object_property_data() at all but
write a "common" function that we pass in all it needs hardcodedly, likebool
RelationIsVisible(Oid relid)
{
return IsVisible_common(RELOID,
Anum_pg_class_relname
Anum_pg_class_relnamespace);
}This would still save a lot of duplicate code.
But again, I don't think the micro-performance really matters here.
I'm seeing that there has been no activity in this thread for almost a
year now, I'm planning to close this in the current commitfest unless
someone is planning to take it forward.
Regards,
Vignesh