diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 6e2fbda..11689f9 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -15279,6 +15279,22 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
collation for
+
+ to_regclass
+
+
+
+ to_regproc
+
+
+
+ to_regoper
+
+
+
+ to_regtype
+
+
lists functions that
extract information from the system catalogs.
@@ -15449,6 +15465,26 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
text
get the collation of the argument
+
+ to_regclass(rel_name)
+ regclass
+ get the oid of the named relation
+
+
+ to_regproc(func_name)
+ regproc
+ get the oid of the named function
+
+
+ to_regoper(operator_name)
+ regoper
+ get the oid of the named operator
+
+
+ to_regtype(type_name)
+ regtype
+ get the oid of the named type
+
@@ -15614,6 +15650,17 @@ SELECT collation for ('foo' COLLATE "de_DE");
is not of a collatable data type, then an error is raised.
+
+ The to_regclass, to_regproc,
+ to_regoper and to_regtype
+ work like casts to regclass>, regproc>, regoper> and
+ regtype>, respectively, except that they return NULL when no
+ object by that name is found, or in the case of
+ to_regproc and to_regoper
+ when more than one object with that name is found, rather than raising
+ an error.
+
+
col_description
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 6c2a5d2..5bf6d28 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -1556,7 +1556,7 @@ OpernameGetOprid(List *names, Oid oprleft, Oid oprright)
* will be InvalidOid for a prefix or postfix oprkind. nargs is 2, too.
*/
FuncCandidateList
-OpernameGetCandidates(List *names, char oprkind)
+OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
{
FuncCandidateList resultList = NULL;
char *resultSpace = NULL;
@@ -1573,7 +1573,9 @@ OpernameGetCandidates(List *names, char oprkind)
if (schemaname)
{
/* use exact schema given */
- namespaceId = LookupExplicitNamespace(schemaname, false);
+ namespaceId = LookupExplicitNamespace(schemaname, missing_schema_ok);
+ if (missing_schema_ok && !OidIsValid(namespaceId))
+ return NULL;
}
else
{
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 99dbd30..a2b712d 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -407,7 +407,7 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
FuncCandidateList clist;
/* Get binary operators of given name */
- clist = OpernameGetCandidates(opname, 'b');
+ clist = OpernameGetCandidates(opname, 'b', false);
/* No operators found? Then fail... */
if (clist != NULL)
@@ -553,7 +553,7 @@ right_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
FuncCandidateList clist;
/* Get postfix operators of given name */
- clist = OpernameGetCandidates(op, 'r');
+ clist = OpernameGetCandidates(op, 'r', false);
/* No operators found? Then fail... */
if (clist != NULL)
@@ -631,7 +631,7 @@ left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
FuncCandidateList clist;
/* Get prefix operators of given name */
- clist = OpernameGetCandidates(op, 'l');
+ clist = OpernameGetCandidates(op, 'l', false);
/* No operators found? Then fail... */
if (clist != NULL)
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index b329dfb..28aae59 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -706,9 +706,12 @@ pts_error_callback(void *arg)
* Given a string that is supposed to be a SQL-compatible type declaration,
* such as "int4" or "integer" or "character varying(32)", parse
* the string and convert it to a type OID and type modifier.
+ * If the type name is not found, return InvalidOid if missing_ok
+ * = true, otherwise raise an error.
*/
void
-parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p)
+parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p,
+ bool missing_ok)
{
StringInfoData buf;
List *raw_parsetree_list;
@@ -717,6 +720,7 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p)
TypeCast *typecast;
TypeName *typeName;
ErrorContextCallback ptserrcontext;
+ Type tup;
/* make sure we give useful error for empty input */
if (strspn(str, " \t\n\r\f") == strlen(str))
@@ -782,7 +786,28 @@ parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p)
if (typeName->setof)
goto fail;
- typenameTypeIdAndMod(NULL, typeName, typeid_p, typmod_p);
+ tup = LookupTypeName(NULL, typeName, typmod_p, missing_ok);
+ if (tup == NULL)
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type \"%s\" does not exist",
+ TypeNameToString(typeName)),
+ parser_errposition(NULL, typeName->location)));
+ *typeid_p = InvalidOid;
+ }
+ else
+ {
+ if (!((Form_pg_type) GETSTRUCT(tup))->typisdefined)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type \"%s\" is only a shell",
+ TypeNameToString(typeName)),
+ parser_errposition(NULL, typeName->location)));
+ *typeid_p = HeapTupleGetOid(tup);
+ ReleaseSysCache(tup);
+ }
pfree(buf.data);
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 5d73562..08f116e 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -45,6 +45,10 @@ static char *format_operator_internal(Oid operator_oid, bool force_qualify);
static char *format_procedure_internal(Oid procedure_oid, bool force_qualify);
static void parseNameAndArgTypes(const char *string, bool allowNone,
List **names, int *nargs, Oid *argtypes);
+static bool regproc_guts(char *pro_name_or_oid, Oid *proid_p, bool missing_ok);
+static bool regoper_guts(char *opr_name_or_oid, RegProcedure *oprid_p, bool missing_ok);
+static bool regclass_guts(char *class_name_or_oid, Oid *classid_p, bool missing_ok);
+static bool regtype_guts(char *typ_name_or_oid, Oid *typid_p, bool missing_ok);
/*****************************************************************************
@@ -63,93 +67,29 @@ Datum
regprocin(PG_FUNCTION_ARGS)
{
char *pro_name_or_oid = PG_GETARG_CSTRING(0);
- RegProcedure result = InvalidOid;
- List *names;
- FuncCandidateList clist;
-
- /* '-' ? */
- if (strcmp(pro_name_or_oid, "-") == 0)
- PG_RETURN_OID(InvalidOid);
-
- /* Numeric OID? */
- if (pro_name_or_oid[0] >= '0' &&
- pro_name_or_oid[0] <= '9' &&
- strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid))
- {
- result = DatumGetObjectId(DirectFunctionCall1(oidin,
- CStringGetDatum(pro_name_or_oid)));
- PG_RETURN_OID(result);
- }
-
- /* Else it's a name, possibly schema-qualified */
-
- /*
- * In bootstrap mode we assume the given name is not schema-qualified, and
- * just search pg_proc for a unique match. This is needed for
- * initializing other system catalogs (pg_namespace may not exist yet, and
- * certainly there are no schemas other than pg_catalog).
- */
- if (IsBootstrapProcessingMode())
- {
- int matches = 0;
- Relation hdesc;
- ScanKeyData skey[1];
- SysScanDesc sysscan;
- HeapTuple tuple;
-
- ScanKeyInit(&skey[0],
- Anum_pg_proc_proname,
- BTEqualStrategyNumber, F_NAMEEQ,
- CStringGetDatum(pro_name_or_oid));
-
- hdesc = heap_open(ProcedureRelationId, AccessShareLock);
- sysscan = systable_beginscan(hdesc, ProcedureNameArgsNspIndexId, true,
- NULL, 1, skey);
-
- while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
- {
- result = (RegProcedure) HeapTupleGetOid(tuple);
- if (++matches > 1)
- break;
- }
+ RegProcedure result;
- systable_endscan(sysscan);
- heap_close(hdesc, AccessShareLock);
-
- if (matches == 0)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("function \"%s\" does not exist", pro_name_or_oid)));
+ regproc_guts(pro_name_or_oid, &result, false);
+ PG_RETURN_OID(result);
+}
- else if (matches > 1)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("more than one function named \"%s\"",
- pro_name_or_oid)));
+/*
+ * to_regproc - converts "proname" to proc OID
+ *
+ * This function differs from regprocin only in returning NULL and not raising
+ * an error when the proname is not found or more than one proname is found.
+ * Note that this is not an I/O function.
+ */
+Datum
+to_regproc(PG_FUNCTION_ARGS)
+{
+ char *pro_name_or_oid = PG_GETARG_CSTRING(0);
+ RegProcedure result;
+ if (regproc_guts(pro_name_or_oid, &result, true))
PG_RETURN_OID(result);
- }
-
- /*
- * Normal case: parse the name into components and see if it matches any
- * pg_proc entries in the current search path.
- */
- names = stringToQualifiedNameList(pro_name_or_oid);
- clist = FuncnameGetCandidates(names, -1, NIL, false, false, false);
-
- if (clist == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("function \"%s\" does not exist", pro_name_or_oid)));
- else if (clist->next != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("more than one function named \"%s\"",
- pro_name_or_oid)));
-
- result = clist->oid;
-
- PG_RETURN_OID(result);
+ else
+ PG_RETURN_NULL();
}
/*
@@ -431,92 +371,29 @@ Datum
regoperin(PG_FUNCTION_ARGS)
{
char *opr_name_or_oid = PG_GETARG_CSTRING(0);
- Oid result = InvalidOid;
- List *names;
- FuncCandidateList clist;
-
- /* '0' ? */
- if (strcmp(opr_name_or_oid, "0") == 0)
- PG_RETURN_OID(InvalidOid);
-
- /* Numeric OID? */
- if (opr_name_or_oid[0] >= '0' &&
- opr_name_or_oid[0] <= '9' &&
- strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid))
- {
- result = DatumGetObjectId(DirectFunctionCall1(oidin,
- CStringGetDatum(opr_name_or_oid)));
- PG_RETURN_OID(result);
- }
-
- /* Else it's a name, possibly schema-qualified */
-
- /*
- * In bootstrap mode we assume the given name is not schema-qualified, and
- * just search pg_operator for a unique match. This is needed for
- * initializing other system catalogs (pg_namespace may not exist yet, and
- * certainly there are no schemas other than pg_catalog).
- */
- if (IsBootstrapProcessingMode())
- {
- int matches = 0;
- Relation hdesc;
- ScanKeyData skey[1];
- SysScanDesc sysscan;
- HeapTuple tuple;
-
- ScanKeyInit(&skey[0],
- Anum_pg_operator_oprname,
- BTEqualStrategyNumber, F_NAMEEQ,
- CStringGetDatum(opr_name_or_oid));
-
- hdesc = heap_open(OperatorRelationId, AccessShareLock);
- sysscan = systable_beginscan(hdesc, OperatorNameNspIndexId, true,
- NULL, 1, skey);
-
- while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
- {
- result = HeapTupleGetOid(tuple);
- if (++matches > 1)
- break;
- }
+ Oid result;
- systable_endscan(sysscan);
- heap_close(hdesc, AccessShareLock);
+ regoper_guts(opr_name_or_oid, &result, false);
+ PG_RETURN_OID(result);
+}
- if (matches == 0)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("operator does not exist: %s", opr_name_or_oid)));
- else if (matches > 1)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("more than one operator named %s",
- opr_name_or_oid)));
+/*
+ * to_regoper - converts "oprname" to operator OID
+ *
+ * This function differs from regoperin only in returning NULL and not raising
+ * an error when the opername is not found or more than one opername is found.
+ * Note that this is not an I/O function.
+ */
+Datum
+to_regoper(PG_FUNCTION_ARGS)
+{
+ char *opr_name_or_oid = PG_GETARG_CSTRING(0);
+ Oid result;
+ if (regoper_guts(opr_name_or_oid, &result, true))
PG_RETURN_OID(result);
- }
-
- /*
- * Normal case: parse the name into components and see if it matches any
- * pg_operator entries in the current search path.
- */
- names = stringToQualifiedNameList(opr_name_or_oid);
- clist = OpernameGetCandidates(names, '\0');
-
- if (clist == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("operator does not exist: %s", opr_name_or_oid)));
- else if (clist->next != NULL)
- ereport(ERROR,
- (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
- errmsg("more than one operator named %s",
- opr_name_or_oid)));
-
- result = clist->oid;
-
- PG_RETURN_OID(result);
+ else
+ PG_RETURN_NULL();
}
/*
@@ -558,7 +435,7 @@ regoperout(PG_FUNCTION_ARGS)
* qualify it.
*/
clist = OpernameGetCandidates(list_make1(makeString(oprname)),
- '\0');
+ '\0', false);
if (clist != NULL && clist->next == NULL &&
clist->oid == oprid)
result = pstrdup(oprname);
@@ -804,72 +681,33 @@ Datum
regclassin(PG_FUNCTION_ARGS)
{
char *class_name_or_oid = PG_GETARG_CSTRING(0);
- Oid result = InvalidOid;
- List *names;
-
- /* '-' ? */
- if (strcmp(class_name_or_oid, "-") == 0)
- PG_RETURN_OID(InvalidOid);
-
- /* Numeric OID? */
- if (class_name_or_oid[0] >= '0' &&
- class_name_or_oid[0] <= '9' &&
- strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid))
- {
- result = DatumGetObjectId(DirectFunctionCall1(oidin,
- CStringGetDatum(class_name_or_oid)));
- PG_RETURN_OID(result);
- }
-
- /* Else it's a name, possibly schema-qualified */
-
- /*
- * In bootstrap mode we assume the given name is not schema-qualified, and
- * just search pg_class for a match. This is needed for initializing
- * other system catalogs (pg_namespace may not exist yet, and certainly
- * there are no schemas other than pg_catalog).
- */
- if (IsBootstrapProcessingMode())
- {
- Relation hdesc;
- ScanKeyData skey[1];
- SysScanDesc sysscan;
- HeapTuple tuple;
-
- ScanKeyInit(&skey[0],
- Anum_pg_class_relname,
- BTEqualStrategyNumber, F_NAMEEQ,
- CStringGetDatum(class_name_or_oid));
-
- hdesc = heap_open(RelationRelationId, AccessShareLock);
- sysscan = systable_beginscan(hdesc, ClassNameNspIndexId, true,
- NULL, 1, skey);
+ Oid result;
- if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
- result = HeapTupleGetOid(tuple);
- else
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_TABLE),
- errmsg("relation \"%s\" does not exist", class_name_or_oid)));
+ if (!regclass_guts(class_name_or_oid, &result, false))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s\" does not exist", class_name_or_oid)));
- /* We assume there can be only one match */
+ PG_RETURN_OID(result);
+}
- systable_endscan(sysscan);
- heap_close(hdesc, AccessShareLock);
+/*
+ * to_regclass - converts "classname" to class OID
+ *
+ * This function differs from regclassin only in returning NULL and not raising
+ * an error when the classname is not found. Note that this is not an I/O
+ * function.
+ */
+Datum
+to_regclass(PG_FUNCTION_ARGS)
+{
+ char *class_name_or_oid = PG_GETARG_CSTRING(0);
+ Oid result;
+ if (regclass_guts(class_name_or_oid, &result, true))
PG_RETURN_OID(result);
- }
-
- /*
- * Normal case: parse the name into components and see if it matches any
- * pg_class entries in the current search path.
- */
- names = stringToQualifiedNameList(class_name_or_oid);
-
- /* We might not even have permissions on this relation; don't lock it. */
- result = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, false);
-
- PG_RETURN_OID(result);
+ else
+ PG_RETURN_NULL();
}
/*
@@ -968,69 +806,33 @@ Datum
regtypein(PG_FUNCTION_ARGS)
{
char *typ_name_or_oid = PG_GETARG_CSTRING(0);
- Oid result = InvalidOid;
- int32 typmod;
-
- /* '-' ? */
- if (strcmp(typ_name_or_oid, "-") == 0)
- PG_RETURN_OID(InvalidOid);
+ Oid result;
- /* Numeric OID? */
- if (typ_name_or_oid[0] >= '0' &&
- typ_name_or_oid[0] <= '9' &&
- strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid))
- {
- result = DatumGetObjectId(DirectFunctionCall1(oidin,
- CStringGetDatum(typ_name_or_oid)));
- PG_RETURN_OID(result);
- }
+ if (!regtype_guts(typ_name_or_oid, &result, false))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("type \"%s\" does not exist", typ_name_or_oid)));
- /* Else it's a type name, possibly schema-qualified or decorated */
-
- /*
- * In bootstrap mode we assume the given name is not schema-qualified, and
- * just search pg_type for a match. This is needed for initializing other
- * system catalogs (pg_namespace may not exist yet, and certainly there
- * are no schemas other than pg_catalog).
- */
- if (IsBootstrapProcessingMode())
- {
- Relation hdesc;
- ScanKeyData skey[1];
- SysScanDesc sysscan;
- HeapTuple tuple;
-
- ScanKeyInit(&skey[0],
- Anum_pg_type_typname,
- BTEqualStrategyNumber, F_NAMEEQ,
- CStringGetDatum(typ_name_or_oid));
-
- hdesc = heap_open(TypeRelationId, AccessShareLock);
- sysscan = systable_beginscan(hdesc, TypeNameNspIndexId, true,
- NULL, 1, skey);
-
- if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
- result = HeapTupleGetOid(tuple);
- else
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("type \"%s\" does not exist", typ_name_or_oid)));
-
- /* We assume there can be only one match */
+ PG_RETURN_OID(result);
+}
- systable_endscan(sysscan);
- heap_close(hdesc, AccessShareLock);
+/*
+ * to_regtype - converts "typename" to type OID
+ *
+ * This function differs from regtypein only in returning NULL and not raising
+ * an error when the typename is not found. Note that this is not an I/O
+ * function.
+ */
+Datum
+to_regtype(PG_FUNCTION_ARGS)
+{
+ char *typ_name_or_oid = PG_GETARG_CSTRING(0);
+ Oid result;
+ if (regtype_guts(typ_name_or_oid, &result, true))
PG_RETURN_OID(result);
- }
-
- /*
- * Normal case: invoke the full parser to deal with special cases such as
- * array syntax.
- */
- parseTypeString(typ_name_or_oid, &result, &typmod);
-
- PG_RETURN_OID(result);
+ else
+ PG_RETURN_NULL();
}
/*
@@ -1388,6 +1190,385 @@ stringToQualifiedNameList(const char *string)
*****************************************************************************/
/*
+ * Guts of regprocin and to_regproc.
+ * If the proname is found, returns true and the OID is stored into *proid_p.
+ * If not found and missing_ok is true, returns false instead of raising an
+ * error.
+ */
+static bool
+regproc_guts(char *pro_name_or_oid, RegProcedure *proid_p, bool missing_ok)
+{
+ List *names;
+ FuncCandidateList clist;
+
+ *proid_p = InvalidOid;
+
+ /* '-' ? */
+ if (strcmp(pro_name_or_oid, "-") == 0)
+ return true;
+
+ /* Numeric OID? */
+ if (pro_name_or_oid[0] >= '0' &&
+ pro_name_or_oid[0] <= '9' &&
+ strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid))
+ {
+ *proid_p = DatumGetObjectId(DirectFunctionCall1(oidin,
+ CStringGetDatum(pro_name_or_oid)));
+ return true;
+ }
+
+ /* Else it's a name, possibly schema-qualified */
+
+ /*
+ * In bootstrap mode we assume the given name is not schema-qualified, and
+ * just search pg_proc for a unique match. This is needed for
+ * initializing other system catalogs (pg_namespace may not exist yet, and
+ * certainly there are no schemas other than pg_catalog).
+ */
+ if (IsBootstrapProcessingMode())
+ {
+ int matches = 0;
+ Relation hdesc;
+ ScanKeyData skey[1];
+ SysScanDesc sysscan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_proc_proname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(pro_name_or_oid));
+
+ hdesc = heap_open(ProcedureRelationId, AccessShareLock);
+ sysscan = systable_beginscan(hdesc, ProcedureNameArgsNspIndexId, true,
+ NULL, 1, skey);
+
+ while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
+ {
+ *proid_p = (RegProcedure) HeapTupleGetOid(tuple);
+ if (++matches > 1)
+ break;
+ }
+
+ systable_endscan(sysscan);
+ heap_close(hdesc, AccessShareLock);
+
+ if (matches == 0)
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function \"%s\" does not exist", pro_name_or_oid)));
+ return false;
+ }
+ else if (matches > 1)
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("more than one function named \"%s\"",
+ pro_name_or_oid)));
+ return false;
+ }
+
+ return true;
+ }
+
+ /*
+ * Normal case: parse the name into components and see if it matches any
+ * pg_proc entries in the current search path.
+ */
+ names = stringToQualifiedNameList(pro_name_or_oid);
+ clist = FuncnameGetCandidates(names, -1, NIL, false, false, missing_ok);
+
+ if (clist == NULL)
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function \"%s\" does not exist", pro_name_or_oid)));
+ return false;
+ }
+ else if (clist->next != NULL)
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("more than one function named \"%s\"",
+ pro_name_or_oid)));
+ return false;
+ }
+
+ *proid_p = clist->oid;
+
+ return true;
+}
+
+/*
+ * Guts of regoperin and to_regoper.
+ * If the operator is found, returns true and the OID is stored into *oprid_p.
+ * If not found and missing_ok is true, returns false instead of raising an
+ * error.
+ */
+static bool
+regoper_guts(char *opr_name_or_oid, Oid *oprid_p, bool missing_ok)
+{
+ List *names;
+ FuncCandidateList clist;
+
+ *oprid_p = InvalidOid;
+
+ /* '0' ? */
+ if (strcmp(opr_name_or_oid, "0") == 0)
+ return true;
+
+ /* Numeric OID? */
+ if (opr_name_or_oid[0] >= '0' &&
+ opr_name_or_oid[0] <= '9' &&
+ strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid))
+ {
+ *oprid_p = DatumGetObjectId(DirectFunctionCall1(oidin,
+ CStringGetDatum(opr_name_or_oid)));
+ return true;
+ }
+
+ /* Else it's a name, possibly schema-qualified */
+
+ /*
+ * In bootstrap mode we assume the given name is not schema-qualified, and
+ * just search pg_operator for a unique match. This is needed for
+ * initializing other system catalogs (pg_namespace may not exist yet, and
+ * certainly there are no schemas other than pg_catalog).
+ */
+ if (IsBootstrapProcessingMode())
+ {
+ int matches = 0;
+ Relation hdesc;
+ ScanKeyData skey[1];
+ SysScanDesc sysscan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_operator_oprname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(opr_name_or_oid));
+
+ hdesc = heap_open(OperatorRelationId, AccessShareLock);
+ sysscan = systable_beginscan(hdesc, OperatorNameNspIndexId, true,
+ NULL, 1, skey);
+
+ while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
+ {
+ *oprid_p = HeapTupleGetOid(tuple);
+ if (++matches > 1)
+ break;
+ }
+
+ systable_endscan(sysscan);
+ heap_close(hdesc, AccessShareLock);
+
+ if (matches == 0)
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("operator does not exist: %s", opr_name_or_oid)));
+ return false;
+ }
+ else if (matches > 1)
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("more than one operator named %s",
+ opr_name_or_oid)));
+ return false;
+ }
+
+ return true;
+ }
+
+ /*
+ * Normal case: parse the name into components and see if it matches any
+ * pg_operator entries in the current search path.
+ */
+ names = stringToQualifiedNameList(opr_name_or_oid);
+ clist = OpernameGetCandidates(names, '\0', missing_ok);
+
+ if (clist == NULL)
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("operator does not exist: %s", opr_name_or_oid)));
+ return false;
+ }
+ else if (clist->next != NULL)
+ {
+ if (!missing_ok)
+ ereport(ERROR,
+ (errcode(ERRCODE_AMBIGUOUS_FUNCTION),
+ errmsg("more than one operator named %s",
+ opr_name_or_oid)));
+ return false;
+ }
+
+ *oprid_p = clist->oid;
+
+ return true;
+}
+
+/*
+ * Guts of regclassin and to_regclass.
+ * If the classname is found, returns true and the OID is stored into *classid_p.
+ * If not found and missing_ok is true, or in bootstrap mode, returns false
+ * instead of raising an error
+ */
+static bool
+regclass_guts(char *class_name_or_oid, Oid *classid_p, bool missing_ok)
+{
+ List *names;
+ bool result = true;
+
+ *classid_p = InvalidOid;
+
+ /* '-' ? */
+ if (strcmp(class_name_or_oid, "-") == 0)
+ return true;
+
+ /* Numeric OID? */
+ if (class_name_or_oid[0] >= '0' &&
+ class_name_or_oid[0] <= '9' &&
+ strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid))
+ {
+ *classid_p = DatumGetObjectId(DirectFunctionCall1(oidin,
+ CStringGetDatum(class_name_or_oid)));
+ return true;
+ }
+
+ /* Else it's a name, possibly schema-qualified */
+
+ /*
+ * In bootstrap mode we assume the given name is not schema-qualified, and
+ * just search pg_class for a match. This is needed for initializing
+ * other system catalogs (pg_namespace may not exist yet, and certainly
+ * there are no schemas other than pg_catalog).
+ */
+ if (IsBootstrapProcessingMode())
+ {
+ Relation hdesc;
+ ScanKeyData skey[1];
+ SysScanDesc sysscan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_class_relname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(class_name_or_oid));
+
+ hdesc = heap_open(RelationRelationId, AccessShareLock);
+ sysscan = systable_beginscan(hdesc, ClassNameNspIndexId, true,
+ NULL, 1, skey);
+
+ if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
+ *classid_p = HeapTupleGetOid(tuple);
+ else
+ result = false;
+
+ /* We assume there can be only one match */
+
+ systable_endscan(sysscan);
+ heap_close(hdesc, AccessShareLock);
+
+ return result;
+ }
+
+ /*
+ * Normal case: parse the name into components and see if it matches any
+ * pg_class entries in the current search path.
+ */
+ names = stringToQualifiedNameList(class_name_or_oid);
+
+ /* We might not even have permissions on this relation; don't lock it. */
+ *classid_p = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, missing_ok);
+
+ return OidIsValid(*classid_p);
+}
+
+/*
+ * Guts of regtypein and to_regtype.
+ * If the type is found, returns true and the OID is stored into *typid_p.
+ * If not found and missing_ok is true, or in bootstrap mode, returns false
+ * instead of raising an error
+ */
+static bool
+regtype_guts(char *typ_name_or_oid, Oid *typid_p, bool missing_ok)
+{
+ int32 typmod;
+ bool result = true;
+
+ *typid_p = InvalidOid;
+
+ /* '-' ? */
+ if (strcmp(typ_name_or_oid, "-") == 0)
+ return true;
+
+ /* Numeric OID? */
+ if (typ_name_or_oid[0] >= '0' &&
+ typ_name_or_oid[0] <= '9' &&
+ strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid))
+ {
+ *typid_p = DatumGetObjectId(DirectFunctionCall1(oidin,
+ CStringGetDatum(typ_name_or_oid)));
+ return true;
+ }
+
+ /* Else it's a type name, possibly schema-qualified or decorated */
+
+ /*
+ * In bootstrap mode we assume the given name is not schema-qualified, and
+ * just search pg_type for a match. This is needed for initializing other
+ * system catalogs (pg_namespace may not exist yet, and certainly there
+ * are no schemas other than pg_catalog).
+ */
+ if (IsBootstrapProcessingMode())
+ {
+ Relation hdesc;
+ ScanKeyData skey[1];
+ SysScanDesc sysscan;
+ HeapTuple tuple;
+
+ ScanKeyInit(&skey[0],
+ Anum_pg_type_typname,
+ BTEqualStrategyNumber, F_NAMEEQ,
+ CStringGetDatum(typ_name_or_oid));
+
+ hdesc = heap_open(TypeRelationId, AccessShareLock);
+ sysscan = systable_beginscan(hdesc, TypeNameNspIndexId, true,
+ NULL, 1, skey);
+
+ if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
+ *typid_p = HeapTupleGetOid(tuple);
+ else
+ result = false;
+
+ /* We assume there can be only one match */
+
+ systable_endscan(sysscan);
+ heap_close(hdesc, AccessShareLock);
+
+ return result;
+ }
+
+ /*
+ * Normal case: invoke the full parser to deal with special cases such as
+ * array syntax.
+ */
+ parseTypeString(typ_name_or_oid, typid_p, &typmod, missing_ok);
+
+ return OidIsValid(*typid_p);
+}
+
+/*
* Given a C string, parse it into a qualified function or operator name
* followed by a parenthesized list of type names. Reduce the
* type names to an array of OIDs (returned into *nargs and *argtypes;
@@ -1523,7 +1704,7 @@ parseNameAndArgTypes(const char *string, bool allowNone, List **names,
else
{
/* Use full parser to resolve the type name */
- parseTypeString(typename, &typeid, &typmod);
+ parseTypeString(typename, &typeid, &typmod, false);
}
if (*nargs >= FUNC_MAX_ARGS)
ereport(ERROR,
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index b30e5e8..2f9d391 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -76,7 +76,8 @@ extern FuncCandidateList FuncnameGetCandidates(List *names,
extern bool FunctionIsVisible(Oid funcid);
extern Oid OpernameGetOprid(List *names, Oid oprleft, Oid oprright);
-extern FuncCandidateList OpernameGetCandidates(List *names, char oprkind);
+extern FuncCandidateList OpernameGetCandidates(List *names, char oprkind,
+ bool missing_schema_ok);
extern bool OperatorIsVisible(Oid oprid);
extern Oid OpclassnameGetOpcid(Oid amid, const char *opcname);
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 334e6b8..21c17a0 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -173,6 +173,8 @@ DATA(insert OID = 44 ( regprocin PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0
DESCR("I/O");
DATA(insert OID = 45 ( regprocout PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2275 "24" _null_ _null_ _null_ _null_ regprocout _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3494 ( to_regproc PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 24 "2275" _null_ _null_ _null_ _null_ to_regproc _null_ _null_ _null_ ));
+DESCR("convert proname to regproc");
DATA(insert OID = 46 ( textin PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "2275" _null_ _null_ _null_ _null_ textin _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 47 ( textout PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2275 "25" _null_ _null_ _null_ _null_ textout _null_ _null_ _null_ ));
@@ -3304,6 +3306,8 @@ DATA(insert OID = 2214 ( regoperin PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2
DESCR("I/O");
DATA(insert OID = 2215 ( regoperout PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2275 "2203" _null_ _null_ _null_ _null_ regoperout _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3492 ( to_regoper PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2203 "2275" _null_ _null_ _null_ _null_ to_regoper _null_ _null_ _null_ ));
+DESCR("convert operator name to regoper");
DATA(insert OID = 2216 ( regoperatorin PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2204 "2275" _null_ _null_ _null_ _null_ regoperatorin _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 2217 ( regoperatorout PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2275 "2204" _null_ _null_ _null_ _null_ regoperatorout _null_ _null_ _null_ ));
@@ -3312,10 +3316,14 @@ DATA(insert OID = 2218 ( regclassin PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2
DESCR("I/O");
DATA(insert OID = 2219 ( regclassout PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2275 "2205" _null_ _null_ _null_ _null_ regclassout _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3495 ( to_regclass PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2205 "2275" _null_ _null_ _null_ _null_ to_regclass _null_ _null_ _null_ ));
+DESCR("convert classname to regclass");
DATA(insert OID = 2220 ( regtypein PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2206 "2275" _null_ _null_ _null_ _null_ regtypein _null_ _null_ _null_ ));
DESCR("I/O");
DATA(insert OID = 2221 ( regtypeout PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2275 "2206" _null_ _null_ _null_ _null_ regtypeout _null_ _null_ _null_ ));
DESCR("I/O");
+DATA(insert OID = 3493 ( to_regtype PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2206 "2275" _null_ _null_ _null_ _null_ to_regtype _null_ _null_ _null_ ));
+DESCR("convert type name to regtype");
DATA(insert OID = 1079 ( regclass PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 2205 "25" _null_ _null_ _null_ _null_ text_regclass _null_ _null_ _null_ ));
DESCR("convert text to regclass");
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index ab73148..fa9cc59 100644
--- a/src/include/parser/parse_type.h
+++ b/src/include/parser/parse_type.h
@@ -47,7 +47,7 @@ extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod);
extern Oid typeidTypeRelid(Oid type_id);
-extern void parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p);
+extern void parseTypeString(const char *str, Oid *typeid_p, int32 *typmod_p, bool missing_ok);
#define ISCOMPLEX(typeid) (typeidTypeRelid(typeid) != InvalidOid)
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index 031a43a..720c831 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -597,6 +597,7 @@ extern char *regexp_fixed_prefix(text *text_re, bool case_insensitive,
/* regproc.c */
extern Datum regprocin(PG_FUNCTION_ARGS);
extern Datum regprocout(PG_FUNCTION_ARGS);
+extern Datum to_regproc(PG_FUNCTION_ARGS);
extern Datum regprocrecv(PG_FUNCTION_ARGS);
extern Datum regprocsend(PG_FUNCTION_ARGS);
extern Datum regprocedurein(PG_FUNCTION_ARGS);
@@ -607,6 +608,7 @@ extern Datum regoperin(PG_FUNCTION_ARGS);
extern Datum regoperout(PG_FUNCTION_ARGS);
extern Datum regoperrecv(PG_FUNCTION_ARGS);
extern Datum regopersend(PG_FUNCTION_ARGS);
+extern Datum to_regoper(PG_FUNCTION_ARGS);
extern Datum regoperatorin(PG_FUNCTION_ARGS);
extern Datum regoperatorout(PG_FUNCTION_ARGS);
extern Datum regoperatorrecv(PG_FUNCTION_ARGS);
@@ -615,10 +617,12 @@ extern Datum regclassin(PG_FUNCTION_ARGS);
extern Datum regclassout(PG_FUNCTION_ARGS);
extern Datum regclassrecv(PG_FUNCTION_ARGS);
extern Datum regclasssend(PG_FUNCTION_ARGS);
+extern Datum to_regclass(PG_FUNCTION_ARGS);
extern Datum regtypein(PG_FUNCTION_ARGS);
extern Datum regtypeout(PG_FUNCTION_ARGS);
extern Datum regtyperecv(PG_FUNCTION_ARGS);
extern Datum regtypesend(PG_FUNCTION_ARGS);
+extern Datum to_regtype(PG_FUNCTION_ARGS);
extern Datum regconfigin(PG_FUNCTION_ARGS);
extern Datum regconfigout(PG_FUNCTION_ARGS);
extern Datum regconfigrecv(PG_FUNCTION_ARGS);
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c
index 7bc29a6..5fff635 100644
--- a/src/pl/plperl/plperl.c
+++ b/src/pl/plperl/plperl.c
@@ -3420,7 +3420,7 @@ plperl_spi_prepare(char *query, int argc, SV **argv)
char *typstr;
typstr = sv2cstr(argv[i]);
- parseTypeString(typstr, &typId, &typmod);
+ parseTypeString(typstr, &typId, &typmod, false);
pfree(typstr);
getTypeInputInfo(typId, &typInput, &typIOParam);
diff --git a/src/pl/plpgsql/src/pl_gram.y b/src/pl/plpgsql/src/pl_gram.y
index c0cb585..1862293 100644
--- a/src/pl/plpgsql/src/pl_gram.y
+++ b/src/pl/plpgsql/src/pl_gram.y
@@ -3462,7 +3462,7 @@ parse_datatype(const char *string, int location)
error_context_stack = &syntax_errcontext;
/* Let the main parser try to parse it under standard SQL rules */
- parseTypeString(string, &type_id, &typmod);
+ parseTypeString(string, &type_id, &typmod, false);
/* Restore former ereport callback */
error_context_stack = syntax_errcontext.previous;
diff --git a/src/pl/plpython/plpy_spi.c b/src/pl/plpython/plpy_spi.c
index 982bf84..060d514 100644
--- a/src/pl/plpython/plpy_spi.c
+++ b/src/pl/plpython/plpy_spi.c
@@ -113,7 +113,7 @@ PLy_spi_prepare(PyObject *self, PyObject *args)
*information for input conversion.
********************************************************/
- parseTypeString(sptr, &typeId, &typmod);
+ parseTypeString(sptr, &typeId, &typmod, false);
typeTup = SearchSysCache1(TYPEOID,
ObjectIdGetDatum(typeId));
diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c
index b3bf65e..2d862a6 100644
--- a/src/pl/tcl/pltcl.c
+++ b/src/pl/tcl/pltcl.c
@@ -2165,7 +2165,7 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp,
typIOParam;
int32 typmod;
- parseTypeString(args[i], &typId, &typmod);
+ parseTypeString(args[i], &typId, &typmod, false);
getTypeInputInfo(typId, &typInput, &typIOParam);
diff --git a/src/test/regress/expected/regproc.out b/src/test/regress/expected/regproc.out
new file mode 100644
index 0000000..9f8d8e8
--- /dev/null
+++ b/src/test/regress/expected/regproc.out
@@ -0,0 +1,237 @@
+--
+-- regproc
+--
+/* If objects exist, return oids */
+-- without schemaname
+SELECT regoper('||/');
+ regoper
+---------
+ ||/
+(1 row)
+
+SELECT regproc('now');
+ regproc
+---------
+ now
+(1 row)
+
+SELECT regclass('pg_class');
+ regclass
+----------
+ pg_class
+(1 row)
+
+SELECT regtype('int4');
+ regtype
+---------
+ integer
+(1 row)
+
+SELECT to_regoper('||/');
+ to_regoper
+------------
+ ||/
+(1 row)
+
+SELECT to_regproc('now');
+ to_regproc
+------------
+ now
+(1 row)
+
+SELECT to_regclass('pg_class');
+ to_regclass
+-------------
+ pg_class
+(1 row)
+
+SELECT to_regtype('int4');
+ to_regtype
+------------
+ integer
+(1 row)
+
+-- with schemaname
+SELECT regoper('pg_catalog.||/');
+ regoper
+---------
+ ||/
+(1 row)
+
+SELECT regproc('pg_catalog.now');
+ regproc
+---------
+ now
+(1 row)
+
+SELECT regclass('pg_catalog.pg_class');
+ regclass
+----------
+ pg_class
+(1 row)
+
+SELECT regtype('pg_catalog.int4');
+ regtype
+---------
+ integer
+(1 row)
+
+SELECT to_regoper('pg_catalog.||/');
+ to_regoper
+------------
+ ||/
+(1 row)
+
+SELECT to_regproc('pg_catalog.now');
+ to_regproc
+------------
+ now
+(1 row)
+
+SELECT to_regclass('pg_catalog.pg_class');
+ to_regclass
+-------------
+ pg_class
+(1 row)
+
+SELECT to_regtype('pg_catalog.int4');
+ to_regtype
+------------
+ integer
+(1 row)
+
+/* If objects don't exist, raise errors. */
+-- without schemaname
+SELECT regoper('||//');
+ERROR: operator does not exist: ||//
+LINE 3: SELECT regoper('||//');
+ ^
+SELECT regproc('know');
+ERROR: function "know" does not exist
+LINE 1: SELECT regproc('know');
+ ^
+SELECT regclass('pg_classes');
+ERROR: relation "pg_classes" does not exist
+LINE 1: SELECT regclass('pg_classes');
+ ^
+SELECT regtype('int3');
+ERROR: type "int3" does not exist
+LINE 1: SELECT regtype('int3');
+ ^
+-- with schemaname
+SELECT regoper('ng_catalog.||/');
+ERROR: schema "ng_catalog" does not exist
+LINE 1: SELECT regoper('ng_catalog.||/');
+ ^
+SELECT regproc('ng_catalog.now');
+ERROR: schema "ng_catalog" does not exist
+LINE 1: SELECT regproc('ng_catalog.now');
+ ^
+SELECT regclass('ng_catalog.pg_class');
+ERROR: schema "ng_catalog" does not exist
+LINE 1: SELECT regclass('ng_catalog.pg_class');
+ ^
+SELECT regtype('ng_catalog.int4');
+ERROR: schema "ng_catalog" does not exist
+LINE 1: SELECT regtype('ng_catalog.int4');
+ ^
+/* If objects don't exist, return NULL with no error. */
+-- without schemaname
+SELECT to_regoper('||//');
+ to_regoper
+------------
+
+(1 row)
+
+SELECT to_regproc('know');
+ to_regproc
+------------
+
+(1 row)
+
+SELECT to_regclass('pg_classes');
+ to_regclass
+-------------
+
+(1 row)
+
+SELECT to_regtype('int3');
+ to_regtype
+------------
+
+(1 row)
+
+-- with schemaname
+SELECT to_regoper('ng_catalog.||/');
+ to_regoper
+------------
+
+(1 row)
+
+SELECT to_regproc('ng_catalog.now');
+ to_regproc
+------------
+
+(1 row)
+
+SELECT to_regclass('ng_catalog.pg_class');
+ to_regclass
+-------------
+
+(1 row)
+
+SELECT to_regtype('ng_catalog.int4');
+ to_regtype
+------------
+
+(1 row)
+
+/* Cast 0 to InvalidOid */
+SELECT regoper('0');
+ regoper
+---------
+ 0
+(1 row)
+
+SELECT regproc('0');
+ regproc
+---------
+ -
+(1 row)
+
+SELECT regclass('0');
+ regclass
+----------
+ -
+(1 row)
+
+SELECT regtype('0');
+ regtype
+---------
+ -
+(1 row)
+
+SELECT to_regoper('0');
+ to_regoper
+------------
+ 0
+(1 row)
+
+SELECT to_regproc('0');
+ to_regproc
+------------
+ -
+(1 row)
+
+SELECT to_regclass('0');
+ to_regclass
+-------------
+ -
+(1 row)
+
+SELECT to_regtype('0');
+ to_regtype
+------------
+ -
+(1 row)
+
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
index c62be2a..c0416f4 100644
--- a/src/test/regress/parallel_schedule
+++ b/src/test/regress/parallel_schedule
@@ -13,7 +13,7 @@ test: tablespace
# ----------
# The first group of parallel tests
# ----------
-test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes pg_lsn
+test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric txid uuid enum money rangetypes pg_lsn regproc
# Depends on things setup during char, varchar and text
test: strings
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
index 885ca9a..16a1905 100644
--- a/src/test/regress/serial_schedule
+++ b/src/test/regress/serial_schedule
@@ -20,6 +20,7 @@ test: enum
test: money
test: rangetypes
test: pg_lsn
+test: regproc
test: strings
test: numerology
test: point
diff --git a/src/test/regress/sql/regproc.sql b/src/test/regress/sql/regproc.sql
new file mode 100644
index 0000000..3cfa726
--- /dev/null
+++ b/src/test/regress/sql/regproc.sql
@@ -0,0 +1,74 @@
+--
+-- regproc
+--
+
+/* If objects exist, return oids */
+
+-- without schemaname
+
+SELECT regoper('||/');
+SELECT regproc('now');
+SELECT regclass('pg_class');
+SELECT regtype('int4');
+
+SELECT to_regoper('||/');
+SELECT to_regproc('now');
+SELECT to_regclass('pg_class');
+SELECT to_regtype('int4');
+
+-- with schemaname
+
+SELECT regoper('pg_catalog.||/');
+SELECT regproc('pg_catalog.now');
+SELECT regclass('pg_catalog.pg_class');
+SELECT regtype('pg_catalog.int4');
+
+SELECT to_regoper('pg_catalog.||/');
+SELECT to_regproc('pg_catalog.now');
+SELECT to_regclass('pg_catalog.pg_class');
+SELECT to_regtype('pg_catalog.int4');
+
+/* If objects don't exist, raise errors. */
+
+-- without schemaname
+
+SELECT regoper('||//');
+SELECT regproc('know');
+SELECT regclass('pg_classes');
+SELECT regtype('int3');
+
+-- with schemaname
+
+SELECT regoper('ng_catalog.||/');
+SELECT regproc('ng_catalog.now');
+SELECT regclass('ng_catalog.pg_class');
+SELECT regtype('ng_catalog.int4');
+
+/* If objects don't exist, return NULL with no error. */
+
+-- without schemaname
+
+SELECT to_regoper('||//');
+SELECT to_regproc('know');
+SELECT to_regclass('pg_classes');
+SELECT to_regtype('int3');
+
+-- with schemaname
+
+SELECT to_regoper('ng_catalog.||/');
+SELECT to_regproc('ng_catalog.now');
+SELECT to_regclass('ng_catalog.pg_class');
+SELECT to_regtype('ng_catalog.int4');
+
+/* Cast 0 to InvalidOid */
+
+SELECT regoper('0');
+SELECT regproc('0');
+SELECT regclass('0');
+SELECT regtype('0');
+
+SELECT to_regoper('0');
+SELECT to_regproc('0');
+SELECT to_regclass('0');
+SELECT to_regtype('0');
+