<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.112 2005/08/24 17:24:17 tgl Exp $
+ $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.113 2005/09/08 20:07:41 tgl Exp $
-->
<chapter id="catalogs">
<entry>operators</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-pltemplate"><structname>pg_pltemplate</structname></link></entry>
+ <entry>template data for procedural languages</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link></entry>
<entry>functions and procedures</entry>
</sect1>
+ <sect1 id="catalog-pg-pltemplate">
+ <title><structname>pg_pltemplate</structname></title>
+
+ <indexterm zone="catalog-pg-pltemplate">
+ <primary>pg_pltemplate</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_pltemplate</structname> stores
+ <quote>template</> information for procedural languages.
+ A template for a language allows the language to be created in a
+ particular database by a simple <command>CREATE LANGUAGE</> command,
+ with no need to specify implementation details.
+ </para>
+
+ <para>
+ Unlike most system catalogs, <structname>pg_pltemplate</structname>
+ is shared across all databases of a cluster: there is only one
+ copy of <structname>pg_pltemplate</structname> per cluster, not
+ one per database. This allows the information to be accessible in
+ each database as it is needed.
+ </para>
+
+ <table>
+ <title><structname>pg_pltemplate</> Columns</title>
+
+ <tgroup cols=4>
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><structfield>tmplname</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>Name of the language this template is for</entry>
+ </row>
+
+ <row>
+ <entry><structfield>tmpltrusted</structfield></entry>
+ <entry><type>boolean</type></entry>
+ <entry></entry>
+ <entry>True if language is considered trusted</entry>
+ </row>
+
+ <row>
+ <entry><structfield>tmplhandler</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>Name of call handler function</entry>
+ </row>
+
+ <row>
+ <entry><structfield>tmplvalidator</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>Name of validator function, or NULL if none</entry>
+ </row>
+
+ <row>
+ <entry><structfield>tmpllibrary</structfield></entry>
+ <entry><type>text</type></entry>
+ <entry></entry>
+ <entry>Path of shared library that implements language</entry>
+ </row>
+
+ <row>
+ <entry><structfield>tmplacl</structfield></entry>
+ <entry><type>aclitem[]</type></entry>
+ <entry></entry>
+ <entry>Access privileges for template (not yet used)</entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ There are not currently any commands that manipulate procedural language
+ templates; to change the built-in information, a superuser must modify
+ the table using ordinary INSERT, DELETE, or UPDATE commands. It is
+ likely that a future release of <productname>PostgreSQL</productname>
+ will offer commands to change the entries in a cleaner fashion.
+ </para>
+
+ <para>
+ When implemented, the <structfield>tmplacl</structfield> field will provide
+ access control for the template itself (i.e., the right to create a
+ language using it), not for the languages created from the template.
+ </para>
+
+ </sect1>
+
+
<sect1 id="catalog-pg-proc">
<title><structname>pg_proc</structname></title>
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_language.sgml,v 1.40 2005/09/05 23:50:48 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_language.sgml,v 1.41 2005/09/08 20:07:41 tgl Exp $
PostgreSQL documentation
-->
<para>
There are two forms of the <command>CREATE LANGUAGE</command> command.
- In the first form, the user merely supplies the name of the desired
+ In the first form, the user supplies just the name of the desired
language, and the <productname>PostgreSQL</productname> server consults
- an internal table to determine the correct parameters. In
- the second form, the user supplies the language parameters along with
- the language name. The second form must be used to create a language
- that is not present in the internal table, but this form is considered
- obsolescent. (It is expected that future releases of
- <productname>PostgreSQL</productname> will replace the internal table
- with a system catalog that can be extended to support additional
- languages.)
+ the <link linkend="catalog-pg-pltemplate"><structname>pg_pltemplate</structname></link>
+ system catalog to determine the correct parameters. In the second form,
+ the user supplies the language parameters along with the language name.
+ The second form can be used to create a language that is not defined in
+ <structname>pg_pltemplate</>, but this approach is considered obsolescent.
</para>
<para>
- When the server finds an entry in its internal table for the given
- language name, it will use the table data even if the given command
- includes language parameters. This behavior simplifies loading of
+ When the server finds an entry in the <structname>pg_pltemplate</> catalog
+ for the given language name, it will use the catalog data even if the
+ command includes language parameters. This behavior simplifies loading of
old dump files, which are likely to contain out-of-date information
about language support functions.
</para>
<para>
The <literal>TRUSTED</> option and the support function name(s) are
- ignored if the server has information about the specified language
- name in its internal table.
+ ignored if the server has an entry for the specified language
+ name in <structname>pg_pltemplate</>.
</para>
</refsect1>
<para>
The call handler function and the validator function (if any)
- must already exist if the server does not have information about
- the language in its internal table. But when there is an entry
- in the internal table, the functions need not already exist;
+ must already exist if the server does not have an entry for the language
+ in <structname>pg_pltemplate</>. But when there is an entry,
+ the functions need not already exist;
they will be automatically defined if not present in the database.
(This can result in <command>CREATE LANGUAGE</> failing, if the
shared library that implements the language is not available in
</para>
<para>
- For a language not known in the server's internal table, a sequence
- such as this is needed:
+ For a language not known in the <structname>pg_pltemplate</> catalog, a
+ sequence such as this is needed:
<programlisting>
CREATE FUNCTION plsample_call_handler() RETURNS language_handler
AS '$libdir/plsample'
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/createlang.sgml,v 1.36 2005/09/05 23:50:48 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/createlang.sgml,v 1.37 2005/09/08 20:07:41 tgl Exp $
PostgreSQL documentation
-->
<para>
<application>createlang</application> is a utility for adding a new
programming language to a <productname>PostgreSQL</productname> database.
- <application>createlang</application> can handle all the languages
- supplied in the default <productname>PostgreSQL</> distribution, but
- not languages provided by other parties. See
+ <application>createlang</application> is just a wrapper around the
<xref linkend="sql-createlanguage" endterm="sql-createlanguage-title">
- for additional information.
+ command, which see for additional information.
</para>
</refsect1>
#
# Makefile for backend/catalog
#
-# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.56 2005/07/14 05:13:39 tgl Exp $
+# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.57 2005/09/08 20:07:41 tgl Exp $
#
#-------------------------------------------------------------------------
pg_operator.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \
- pg_namespace.h pg_conversion.h pg_database.h pg_shdepend.h \
- pg_authid.h pg_auth_members.h pg_tablespace.h pg_depend.h \
+ pg_namespace.h pg_conversion.h pg_depend.h \
+ pg_database.h pg_tablespace.h pg_pltemplate.h \
+ pg_authid.h pg_auth_members.h pg_shdepend.h \
indexing.h \
)
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.66 2005/08/22 17:38:20 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.67 2005/09/08 20:07:41 tgl Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
+#include "commands/proclang.h"
#include "miscadmin.h"
#include "optimizer/cost.h"
#include "parser/parse_func.h"
PointerGetDatum(languageName),
0, 0, 0);
if (!HeapTupleIsValid(languageTuple))
- /* Add any new languages to this list to invoke the hint. */
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("language \"%s\" does not exist", languageName),
- (strcmp(languageName, "plperl") == 0 ||
- strcmp(languageName, "plperlu") == 0 ||
- strcmp(languageName, "plpgsql") == 0 ||
- strcmp(languageName, "plpythonu") == 0 ||
- strcmp(languageName, "pltcl") == 0 ||
- strcmp(languageName, "pltclu") == 0) ?
- errhint("You need to use \"createlang\" to load the language into the database.") : 0));
+ (PLTemplateExists(languageName) ?
+ errhint("Use CREATE LANGUAGE to load the language into the database.") : 0)));
languageOid = HeapTupleGetOid(languageTuple);
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.61 2005/09/05 23:50:48 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/proclang.c,v 1.62 2005/09/08 20:07:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_pltemplate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/proclang.h"
typedef struct
{
- char *lanname; /* PL name */
- bool lantrusted; /* trusted? */
- char *lanhandler; /* name of handler function */
- char *lanvalidator; /* name of validator function, or NULL */
- char *lanlibrary; /* path of shared library */
+ bool tmpltrusted; /* trusted? */
+ char *tmplhandler; /* name of handler function */
+ char *tmplvalidator; /* name of validator function, or NULL */
+ char *tmpllibrary; /* path of shared library */
} PLTemplate;
static void create_proc_lang(const char *languageName,
{
List *funcname;
+ /*
+ * Give a notice if we are ignoring supplied parameters.
+ */
+ if (stmt->plhandler)
+ ereport(NOTICE,
+ (errmsg("using pg_pltemplate information instead of CREATE LANGUAGE parameters")));
+
/*
* Find or create the handler function, which we force to be in
* the pg_catalog schema. If already present, it must have the
* correct return type.
*/
- funcname = SystemFuncName(pltemplate->lanhandler);
+ funcname = SystemFuncName(pltemplate->tmplhandler);
handlerOid = LookupFuncName(funcname, 0, funcargtypes, true);
if (OidIsValid(handlerOid))
{
}
else
{
- handlerOid = ProcedureCreate(pltemplate->lanhandler,
+ handlerOid = ProcedureCreate(pltemplate->tmplhandler,
PG_CATALOG_NAMESPACE,
false, /* replace */
false, /* returnsSet */
LANGUAGE_HANDLEROID,
ClanguageId,
F_FMGR_C_VALIDATOR,
- pltemplate->lanhandler,
- pltemplate->lanlibrary,
+ pltemplate->tmplhandler,
+ pltemplate->tmpllibrary,
false, /* isAgg */
false, /* security_definer */
false, /* isStrict */
* Likewise for the validator, if required; but we don't care about
* its return type.
*/
- if (pltemplate->lanvalidator)
+ if (pltemplate->tmplvalidator)
{
- funcname = SystemFuncName(pltemplate->lanvalidator);
+ funcname = SystemFuncName(pltemplate->tmplvalidator);
funcargtypes[0] = OIDOID;
valOid = LookupFuncName(funcname, 1, funcargtypes, true);
if (!OidIsValid(valOid))
{
- valOid = ProcedureCreate(pltemplate->lanvalidator,
+ valOid = ProcedureCreate(pltemplate->tmplvalidator,
PG_CATALOG_NAMESPACE,
false, /* replace */
false, /* returnsSet */
VOIDOID,
ClanguageId,
F_FMGR_C_VALIDATOR,
- pltemplate->lanvalidator,
- pltemplate->lanlibrary,
+ pltemplate->tmplvalidator,
+ pltemplate->tmpllibrary,
false, /* isAgg */
false, /* security_definer */
false, /* isStrict */
/* ok, create it */
create_proc_lang(languageName, handlerOid, valOid,
- pltemplate->lantrusted);
+ pltemplate->tmpltrusted);
}
else
{
* No template, so use the provided information. If there's
* no handler clause, the user is trying to rely on a template
* that we don't have, so complain accordingly.
- *
- * XXX In 8.2, replace the detail message with a hint to look in
- * pg_pltemplate.
*/
if (!stmt->plhandler)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("unsupported language \"%s\"",
languageName),
- errdetail("Supported languages are plpgsql, pltcl, pltclu, "
- "plperl, plperlu, and plpythonu.")));
+ errhint("The supported languages are listed in the pg_pltemplate system catalog.")));
/*
* Lookup the PL handler function and check that it is of the expected
/*
* Look to see if we have template information for the given language name.
- *
- * XXX for PG 8.1, the template info is hard-wired. This is to be replaced
- * by a shared system catalog in 8.2.
- *
- * XXX if you add languages to this list, add them also to the errdetail
- * message above and the list in functioncmds.c. Those hard-wired lists
- * should go away in 8.2, also.
*/
static PLTemplate *
find_language_template(const char *languageName)
{
- static PLTemplate templates[] = {
- { "plpgsql", true, "plpgsql_call_handler", "plpgsql_validator",
- "$libdir/plpgsql" },
- { "pltcl", true, "pltcl_call_handler", NULL,
- "$libdir/pltcl" },
- { "pltclu", false, "pltclu_call_handler", NULL,
- "$libdir/pltcl" },
- { "plperl", true, "plperl_call_handler", "plperl_validator",
- "$libdir/plperl" },
- { "plperlu", false, "plperl_call_handler", "plperl_validator",
- "$libdir/plperl" },
- { "plpythonu", false, "plpython_call_handler", NULL,
- "$libdir/plpython" },
- { NULL, false, NULL, NULL, NULL }
- };
-
- PLTemplate *ptr;
-
- for (ptr = templates; ptr->lanname != NULL; ptr++)
+ PLTemplate *result;
+ Relation rel;
+ SysScanDesc scan;
+ ScanKeyData key;
+ HeapTuple tup;
+
+ rel = heap_open(PLTemplateRelationId, AccessShareLock);
+
+ ScanKeyInit(&key,
+ Anum_pg_pltemplate_tmplname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ NameGetDatum(languageName));
+ scan = systable_beginscan(rel, PLTemplateNameIndexId, true,
+ SnapshotNow, 1, &key);
+
+ tup = systable_getnext(scan);
+ if (HeapTupleIsValid(tup))
{
- if (strcmp(languageName, ptr->lanname) == 0)
- return ptr;
+ Form_pg_pltemplate tmpl = (Form_pg_pltemplate) GETSTRUCT(tup);
+ Datum datum;
+ bool isnull;
+
+ result = (PLTemplate *) palloc0(sizeof(PLTemplate));
+ result->tmpltrusted = tmpl->tmpltrusted;
+
+ /* Remaining fields are variable-width so we need heap_getattr */
+ datum = heap_getattr(tup, Anum_pg_pltemplate_tmplhandler,
+ RelationGetDescr(rel), &isnull);
+ if (!isnull)
+ result->tmplhandler =
+ DatumGetCString(DirectFunctionCall1(textout, datum));
+
+ datum = heap_getattr(tup, Anum_pg_pltemplate_tmplvalidator,
+ RelationGetDescr(rel), &isnull);
+ if (!isnull)
+ result->tmplvalidator =
+ DatumGetCString(DirectFunctionCall1(textout, datum));
+
+ datum = heap_getattr(tup, Anum_pg_pltemplate_tmpllibrary,
+ RelationGetDescr(rel), &isnull);
+ if (!isnull)
+ result->tmpllibrary =
+ DatumGetCString(DirectFunctionCall1(textout, datum));
+
+ /* Ignore template if handler or library info is missing */
+ if (!result->tmplhandler || !result->tmpllibrary)
+ result = NULL;
}
- return NULL;
+ else
+ result = NULL;
+
+ systable_endscan(scan);
+
+ heap_close(rel, AccessShareLock);
+
+ return result;
+}
+
+
+/*
+ * This just returns TRUE if we have a valid template for a given language
+ */
+bool
+PLTemplateExists(const char *languageName)
+{
+ return (find_language_template(languageName) != NULL);
}
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.300 2005/08/15 23:00:14 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.301 2005/09/08 20:07:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200508152
+#define CATALOG_VERSION_NO 200509081
#endif
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
- * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.90 2005/07/14 05:13:42 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.91 2005/09/08 20:07:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
DECLARE_UNIQUE_INDEX(pg_auth_members_member_role_index,2695, on pg_auth_members using btree(member oid_ops, roleid oid_ops));
#define AuthMemMemRoleIndexId 2695
+DECLARE_UNIQUE_INDEX(pg_autovacuum_vacrelid_index,1250, on pg_autovacuum using btree(vacrelid oid_ops));
+#define AutovacuumRelidIndexId 1250
+
DECLARE_UNIQUE_INDEX(pg_cast_oid_index,2660, on pg_cast using btree(oid oid_ops));
#define CastOidIndexId 2660
DECLARE_UNIQUE_INDEX(pg_cast_source_target_index,2661, on pg_cast using btree(castsource oid_ops, casttarget oid_ops));
DECLARE_UNIQUE_INDEX(pg_operator_oprname_l_r_n_index,2689, on pg_operator using btree(oprname name_ops, oprleft oid_ops, oprright oid_ops, oprnamespace oid_ops));
#define OperatorNameNspIndexId 2689
+DECLARE_UNIQUE_INDEX(pg_pltemplate_name_index,1137, on pg_pltemplate using btree(tmplname name_ops));
+#define PLTemplateNameIndexId 1137
+
DECLARE_UNIQUE_INDEX(pg_proc_oid_index,2690, on pg_proc using btree(oid oid_ops));
#define ProcedureOidIndexId 2690
DECLARE_UNIQUE_INDEX(pg_proc_proname_args_nsp_index,2691, on pg_proc using btree(proname name_ops, proargtypes oidvector_ops, pronamespace oid_ops));
DECLARE_UNIQUE_INDEX(pg_type_typname_nsp_index,2704, on pg_type using btree(typname name_ops, typnamespace oid_ops));
#define TypeNameNspIndexId 2704
-DECLARE_UNIQUE_INDEX(pg_autovacuum_vacrelid_index,1250, on pg_autovacuum using btree(vacrelid oid_ops));
-#define AutovacuumRelidIndexId 1250
-
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * pg_pltemplate.h
+ * definition of the system "PL template" relation (pg_pltemplate)
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * $PostgreSQL: pgsql/src/include/catalog/pg_pltemplate.h,v 1.1 2005/09/08 20:07:42 tgl Exp $
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PLTEMPLATE_H
+#define PG_PLTEMPLATE_H
+
+/* ----------------
+ * postgres.h contains the system type definitions and the
+ * CATALOG(), BKI_BOOTSTRAP and DATA() sugar words so this file
+ * can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+
+/* ----------------
+ * pg_pltemplate definition. cpp turns this into
+ * typedef struct FormData_pg_pltemplate
+ * ----------------
+ */
+#define PLTemplateRelationId 1136
+
+CATALOG(pg_pltemplate,1136) BKI_SHARED_RELATION BKI_WITHOUT_OIDS
+{
+ NameData tmplname; /* name of PL */
+ bool tmpltrusted; /* PL is trusted? */
+ text tmplhandler; /* name of call handler function */
+ text tmplvalidator; /* name of validator function, or NULL */
+ text tmpllibrary; /* path of shared library */
+ aclitem tmplacl[1]; /* access privileges for template */
+} FormData_pg_pltemplate;
+
+/* ----------------
+ * Form_pg_pltemplate corresponds to a pointer to a row with
+ * the format of pg_pltemplate relation.
+ * ----------------
+ */
+typedef FormData_pg_pltemplate *Form_pg_pltemplate;
+
+/* ----------------
+ * compiler constants for pg_pltemplate
+ * ----------------
+ */
+#define Natts_pg_pltemplate 6
+#define Anum_pg_pltemplate_tmplname 1
+#define Anum_pg_pltemplate_tmpltrusted 2
+#define Anum_pg_pltemplate_tmplhandler 3
+#define Anum_pg_pltemplate_tmplvalidator 4
+#define Anum_pg_pltemplate_tmpllibrary 5
+#define Anum_pg_pltemplate_tmplacl 6
+
+
+/* ----------------
+ * initial contents of pg_pltemplate
+ * ----------------
+ */
+
+DATA(insert ( "plpgsql" t "plpgsql_call_handler" "plpgsql_validator" "$libdir/plpgsql" _null_ ));
+DATA(insert ( "pltcl" t "pltcl_call_handler" _null_ "$libdir/pltcl" _null_ ));
+DATA(insert ( "pltclu" f "pltclu_call_handler" _null_ "$libdir/pltcl" _null_ ));
+DATA(insert ( "plperl" t "plperl_call_handler" "plperl_validator" "$libdir/plperl" _null_ ));
+DATA(insert ( "plperlu" f "plperl_call_handler" "plperl_validator" "$libdir/plperl" _null_ ));
+DATA(insert ( "plpythonu" f "plpython_call_handler" _null_ "$libdir/plpython" _null_ ));
+
+#endif /* PG_PLTEMPLATE_H */
extern void DropProceduralLanguage(DropPLangStmt *stmt);
extern void DropProceduralLanguageById(Oid langOid);
extern void RenameLanguage(const char *oldname, const char *newname);
+extern bool PLTemplateExists(const char *languageName);
#endif /* PROCLANG_H */
pg_namespace | t
pg_opclass | t
pg_operator | t
+ pg_pltemplate | t
pg_proc | t
pg_rewrite | t
pg_shdepend | t
shighway | t
tenk1 | t
tenk2 | t
-(57 rows)
+(58 rows)
--
-- another sanity check: every system catalog that has OIDs should have