PATCH: Expose generate_qualified_relation_name functionality
I'm regularly surprised that the only non-static function we seem to
have for getting a relation name by oid is get_rel_name. It doesn't
handle schema qualification at all, and it returns NULL rather than
ERROR'ing.
Doing it correctly involves interacting with the syscache, calling
RelationIsVisible and calling get_namespace_name if needed, then
passing the result to quote_qualified_identifier.
so here's what I propose to do:
Add get_qualified_relation_name(bool force_qualified, bool missing_ok)
to ruleutils.c and builtins.h.
(Unless there's somewhere better? I wanted to put it in lsyscache.c
alongside get_rel_name, but it uses quote_qualified_identifier,
StringInfo, etc, so it doesn't fit well in lsyscache.c.)
Replace generate_qualified_relation_name in ruleutils.c with a call to
get_qualified_relation_name(relid, true, false) .
Add comments to RelationGetRelationName, get_rel_name and regclassout
pointing to get_qualified_relation_name.
The only part I don't like here is the two boolean arguments, since
they're ugly to read. But inventing a new flags field seemed a bit
heavy for such a trivial task.
I had a quick look through the codebase for places where this pattern
is repeated and found astonishingly few. In most places we just use
get_rel_name and hope the user can figure out any ambiguity.
I did notice that in heap_copy_data and AlterTableMoveAll we manually
schema-qualify a relation name and fail to call
quote_qualified_identifier, so it's unclear if we mean a relation
named "my.relation" or the relation "relation" in schema "my". But in
diagnostic output, meh, whatever.
Arguably regclassout does the same thing as
get_qualified_relation_name, but I didn't replace it because it cares
about IsBootstrapProcessingMode().
(Prompted by https://dba.stackexchange.com/q/187788/7788)
--
Craig Ringer http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
Attachments:
v1-0001-Add-get_qualified_relation_name.patchtext/x-patch; charset=US-ASCII; name=v1-0001-Add-get_qualified_relation_name.patchDownload
From 011cd6e08d6d29854db637d6beb8709615f376cb Mon Sep 17 00:00:00 2001
From: Craig Ringer <craig@2ndquadrant.com>
Date: Fri, 6 Oct 2017 10:48:17 +0800
Subject: [PATCH v1] Add get_qualified_relation_name
It was surprisingly hard to just get a table name in PostgreSQL at the
C level.
To simplify that, expose the functionality of generate_qualified_relation_name
and generate_relation_name to callers in the wider codebase and to extensions
in the form of a new get_qualified_relation_name function.
---
src/backend/utils/adt/regproc.c | 2 +
src/backend/utils/adt/ruleutils.c | 81 +++++++++++++++++++++++++++----------
src/backend/utils/cache/lsyscache.c | 5 ++-
src/include/utils/builtins.h | 2 +
src/include/utils/rel.h | 4 ++
5 files changed, 70 insertions(+), 24 deletions(-)
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 6fe81fa..691efa8 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -967,6 +967,8 @@ to_regclass(PG_FUNCTION_ARGS)
/*
* regclassout - converts class OID to "class_name"
+ *
+ * (See get_qualified_relation_name for a direct-callable version).
*/
Datum
regclassout(PG_FUNCTION_ARGS)
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index eb01f35..0eccc02 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -10483,6 +10483,9 @@ quote_qualified_identifier(const char *qualifier,
*
* This differs from the underlying get_rel_name() function in that it will
* throw error instead of silently returning NULL if the OID is bad.
+ *
+ * The returned relation name is not guaranteed to be unique outside the
+ * schema, so it is frequently preferable to use get_qualified_relation_name.
*/
static char *
get_relation_name(Oid relid)
@@ -10495,6 +10498,56 @@ get_relation_name(Oid relid)
}
/*
+ * get_qualified_relation_name
+ *
+ * Get a relation name as a palloc'd string in the current memory context.
+ *
+ * If the relation is not on the current search path, or if force_qualify is
+ * true, the namespace for the relation is prepended.
+ *
+ * ERROR's on a missing relation unless missing_ok is set, in which case returns
+ * NULL.
+ *
+ * The quoting rules used are the same as those for regclass quoting: each
+ * component is quoted if necessary to prevent ambiguity.
+ */
+char *
+get_qualified_relation_name(Oid relid, bool force_qualify, bool missing_ok)
+{
+ HeapTuple tp;
+ Form_pg_class reltup;
+ char *relname;
+ char *nspname = NULL;
+ char *result;
+
+ tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tp))
+ {
+ if (missing_ok)
+ return NULL;
+ else
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+ }
+
+ reltup = (Form_pg_class) GETSTRUCT(tp);
+ relname = NameStr(reltup->relname);
+
+ if (force_qualify || !RelationIsVisible(relid))
+ {
+ nspname = get_namespace_name(reltup->relnamespace);
+ if (!nspname)
+ elog(ERROR, "cache lookup failed for namespace %u",
+ reltup->relnamespace);
+ }
+
+ result = quote_qualified_identifier(nspname, relname);
+
+ ReleaseSysCache(tp);
+
+ return result;
+}
+
+/*
* generate_relation_name
* Compute the name to display for a relation specified by OID
*
@@ -10503,6 +10556,11 @@ get_relation_name(Oid relid)
* If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
* We will forcibly qualify the relation name if it equals any CTE name
* visible in the namespace list.
+ *
+ * This doesn't use get_qualified_relation_name because it has
+ * to do CTE namespace clash checks to decide whether to force-qualify
+ * the name. For that it needs the namespace, and we don't want to do
+ * a second syscache lookup by calling get_qualified_relation_name.
*/
static char *
generate_relation_name(Oid relid, List *namespaces)
@@ -10567,28 +10625,7 @@ generate_relation_name(Oid relid, List *namespaces)
static char *
generate_qualified_relation_name(Oid relid)
{
- HeapTuple tp;
- Form_pg_class reltup;
- char *relname;
- char *nspname;
- char *result;
-
- tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
- if (!HeapTupleIsValid(tp))
- elog(ERROR, "cache lookup failed for relation %u", relid);
- reltup = (Form_pg_class) GETSTRUCT(tp);
- relname = NameStr(reltup->relname);
-
- nspname = get_namespace_name(reltup->relnamespace);
- if (!nspname)
- elog(ERROR, "cache lookup failed for namespace %u",
- reltup->relnamespace);
-
- result = quote_qualified_identifier(nspname, relname);
-
- ReleaseSysCache(tp);
-
- return result;
+ return get_qualified_relation_name(relid, true, false);
}
/*
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 82763f8..0c8fdfc 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -1719,8 +1719,9 @@ get_relnatts(Oid relid)
*
* Returns a palloc'd copy of the string, or NULL if no such relation.
*
- * NOTE: since relation name is not unique, be wary of code that uses this
- * for anything except preparing error messages.
+ * NOTE: since relation name is not unique, be wary of code that uses this for
+ * anything except preparing error messages. See get_qualified_relation_name
+ * for a safer option.
*/
char *
get_rel_name(Oid relid)
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 762532f..105daef 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -78,6 +78,8 @@ extern bool quote_all_identifiers;
extern const char *quote_identifier(const char *ident);
extern char *quote_qualified_identifier(const char *qualifier,
const char *ident);
+extern char *get_qualified_relation_name(Oid relid, bool force_qualify,
+ bool missing_ok);
/* varchar.c */
extern int bpchartruelen(char *s, int len);
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index 4bc61e5..f68142c 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -432,6 +432,8 @@ typedef struct ViewOptions
* Returns the rel's name.
*
* Note that the name is only unique within the containing namespace.
+ *
+ * See also get_rel_name(...), and get_qualified_relation_name(...).
*/
#define RelationGetRelationName(relation) \
(NameStr((relation)->rd_rel->relname))
@@ -439,6 +441,8 @@ typedef struct ViewOptions
/*
* RelationGetNamespace
* Returns the rel's namespace OID.
+ *
+ * See also get_namespace_name(...).
*/
#define RelationGetNamespace(relation) \
((relation)->rd_rel->relnamespace)
--
2.9.5