postgresql/src/backend/utils/adt/regproc.c
Tom Lane 83a5641945 Provide more-specific error details/hints for function lookup failures.
Up to now we've contented ourselves with a one-size-fits-all error
hint when we fail to find any match to a function or procedure call.
That was mostly okay in the beginning, but it was never great, and
since the introduction of named arguments it's really not adequate.
We at least ought to distinguish "function name doesn't exist" from
"function name exists, but not with those argument names".  And the
rules for named-argument matching are arcane enough that some more
detail seems warranted if we match the argument names but the call
still doesn't work.

This patch creates a framework for dealing with these problems:
FuncnameGetCandidates and related code will now pass back a bitmask of
flags showing how far the match succeeded.  This allows a considerable
amount of granularity in the reports.  The set-bits-in-a-bitmask
approach means that when there are multiple candidate functions, the
report will reflect the match(es) that got the furthest, which seems
correct.  Also, we can avoid mentioning "maybe add casts" unless
failure to match argument types is actually the issue.

Extend the same return-a-bitmask approach to OpernameGetCandidates.
The issues around argument names don't apply to operator syntax,
but it still seems worth distinguishing between "there is no
operator of that name" and "we couldn't match the argument types".

While at it, adjust these messages and related ones to more strictly
separate "detail" from "hint", following our message style guidelines'
distinction between those.

Reported-by: Dominique Devienne <ddevienne@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Reviewed-by: Robert Haas <robertmhaas@gmail.com>
Discussion: https://postgr.es/m/1756041.1754616558@sss.pgh.pa.us
2025-09-16 12:17:02 -04:00

2163 lines
52 KiB
C

/*-------------------------------------------------------------------------
*
* regproc.c
* Functions for the built-in types regproc, regclass, regtype, etc.
*
* These types are all binary-compatible with type Oid, and rely on Oid
* for comparison and so forth. Their only interesting behavior is in
* special I/O conversion routines.
*
*
* Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/utils/adt/regproc.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <ctype.h>
#include "access/htup_details.h"
#include "catalog/namespace.h"
#include "catalog/pg_class.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_database.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_type.h"
#include "lib/stringinfo.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
#include "nodes/miscnodes.h"
#include "parser/parse_type.h"
#include "parser/scansup.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/regproc.h"
#include "utils/syscache.h"
#include "utils/varlena.h"
static bool parseNumericOid(char *string, Oid *result, Node *escontext);
static bool parseDashOrOid(char *string, Oid *result, Node *escontext);
static bool parseNameAndArgTypes(const char *string, bool allowNone,
List **names, int *nargs, Oid *argtypes,
Node *escontext);
/*****************************************************************************
* USER I/O ROUTINES *
*****************************************************************************/
/*
* regprocin - converts "proname" to proc OID
*
* We also accept a numeric OID, for symmetry with the output routine.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_proc entry.
*/
Datum
regprocin(PG_FUNCTION_ARGS)
{
char *pro_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
RegProcedure result;
List *names;
FuncCandidateList clist;
int fgc_flags;
/* Handle "-" or numeric OID */
if (parseDashOrOid(pro_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* Else it's a name, possibly schema-qualified */
/*
* We should never get here in bootstrap mode, as all references should
* have been resolved by genbki.pl.
*/
if (IsBootstrapProcessingMode())
elog(ERROR, "regproc values must be OIDs in bootstrap mode");
/*
* 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, escontext);
if (names == NIL)
PG_RETURN_NULL();
clist = FuncnameGetCandidates(names, -1, NIL, false, false, false, true,
&fgc_flags);
if (clist == NULL)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function \"%s\" does not exist", pro_name_or_oid)));
else if (clist->next != NULL)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("more than one function named \"%s\"",
pro_name_or_oid)));
result = clist->oid;
PG_RETURN_OID(result);
}
/*
* to_regproc - converts "proname" to proc OID
*
* If the name is not found, we return NULL.
*/
Datum
to_regproc(PG_FUNCTION_ARGS)
{
char *pro_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
Datum result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!DirectInputFunctionCallSafe(regprocin, pro_name,
InvalidOid, -1,
(Node *) &escontext,
&result))
PG_RETURN_NULL();
PG_RETURN_DATUM(result);
}
/*
* regprocout - converts proc OID to "pro_name"
*/
Datum
regprocout(PG_FUNCTION_ARGS)
{
RegProcedure proid = PG_GETARG_OID(0);
char *result;
HeapTuple proctup;
if (proid == InvalidOid)
{
result = pstrdup("-");
PG_RETURN_CSTRING(result);
}
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(proid));
if (HeapTupleIsValid(proctup))
{
Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
char *proname = NameStr(procform->proname);
/*
* In bootstrap mode, skip the fancy namespace stuff and just return
* the proc name. (This path is only needed for debugging output
* anyway.)
*/
if (IsBootstrapProcessingMode())
result = pstrdup(proname);
else
{
char *nspname;
FuncCandidateList clist;
int fgc_flags;
/*
* Would this proc be found (uniquely!) by regprocin? If not,
* qualify it.
*/
clist = FuncnameGetCandidates(list_make1(makeString(proname)),
-1, NIL, false, false, false, false,
&fgc_flags);
if (clist != NULL && clist->next == NULL &&
clist->oid == proid)
nspname = NULL;
else
nspname = get_namespace_name(procform->pronamespace);
result = quote_qualified_identifier(nspname, proname);
}
ReleaseSysCache(proctup);
}
else
{
/* If OID doesn't match any pg_proc entry, return it numerically */
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", proid);
}
PG_RETURN_CSTRING(result);
}
/*
* regprocrecv - converts external binary format to regproc
*/
Datum
regprocrecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regprocsend - converts regproc to binary format
*/
Datum
regprocsend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* regprocedurein - converts "proname(args)" to proc OID
*
* We also accept a numeric OID, for symmetry with the output routine.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_proc entry.
*/
Datum
regprocedurein(PG_FUNCTION_ARGS)
{
char *pro_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
RegProcedure result;
List *names;
int nargs;
Oid argtypes[FUNC_MAX_ARGS];
FuncCandidateList clist;
int fgc_flags;
/* Handle "-" or numeric OID */
if (parseDashOrOid(pro_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
elog(ERROR, "regprocedure values must be OIDs in bootstrap mode");
/*
* Else it's a name and arguments. Parse the name and arguments, look up
* potential matches in the current namespace search list, and scan to see
* which one exactly matches the given argument types. (There will not be
* more than one match.)
*/
if (!parseNameAndArgTypes(pro_name_or_oid, false,
&names, &nargs, argtypes,
escontext))
PG_RETURN_NULL();
clist = FuncnameGetCandidates(names, nargs, NIL, false, false, false, true,
&fgc_flags);
for (; clist; clist = clist->next)
{
if (memcmp(clist->args, argtypes, nargs * sizeof(Oid)) == 0)
break;
}
if (clist == NULL)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function \"%s\" does not exist", pro_name_or_oid)));
result = clist->oid;
PG_RETURN_OID(result);
}
/*
* to_regprocedure - converts "proname(args)" to proc OID
*
* If the name is not found, we return NULL.
*/
Datum
to_regprocedure(PG_FUNCTION_ARGS)
{
char *pro_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
Datum result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!DirectInputFunctionCallSafe(regprocedurein, pro_name,
InvalidOid, -1,
(Node *) &escontext,
&result))
PG_RETURN_NULL();
PG_RETURN_DATUM(result);
}
/*
* format_procedure - converts proc OID to "pro_name(args)"
*
* This exports the useful functionality of regprocedureout for use
* in other backend modules. The result is a palloc'd string.
*/
char *
format_procedure(Oid procedure_oid)
{
return format_procedure_extended(procedure_oid, 0);
}
char *
format_procedure_qualified(Oid procedure_oid)
{
return format_procedure_extended(procedure_oid, FORMAT_PROC_FORCE_QUALIFY);
}
/*
* format_procedure_extended - converts procedure OID to "pro_name(args)"
*
* This exports the useful functionality of regprocedureout for use
* in other backend modules. The result is a palloc'd string, or NULL.
*
* Routine to produce regprocedure names; see format_procedure above.
*
* The following bits in 'flags' modify the behavior:
* - FORMAT_PROC_INVALID_AS_NULL
* if the procedure OID is invalid or unknown, return NULL instead
* of the numeric OID.
* - FORMAT_PROC_FORCE_QUALIFY
* always schema-qualify procedure names, regardless of search_path
*/
char *
format_procedure_extended(Oid procedure_oid, bits16 flags)
{
char *result;
HeapTuple proctup;
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
if (HeapTupleIsValid(proctup))
{
Form_pg_proc procform = (Form_pg_proc) GETSTRUCT(proctup);
char *proname = NameStr(procform->proname);
int nargs = procform->pronargs;
int i;
char *nspname;
StringInfoData buf;
/* XXX no support here for bootstrap mode */
Assert(!IsBootstrapProcessingMode());
initStringInfo(&buf);
/*
* Would this proc be found (given the right args) by regprocedurein?
* If not, or if caller requests it, we need to qualify it.
*/
if ((flags & FORMAT_PROC_FORCE_QUALIFY) == 0 &&
FunctionIsVisible(procedure_oid))
nspname = NULL;
else
nspname = get_namespace_name(procform->pronamespace);
appendStringInfo(&buf, "%s(",
quote_qualified_identifier(nspname, proname));
for (i = 0; i < nargs; i++)
{
Oid thisargtype = procform->proargtypes.values[i];
if (i > 0)
appendStringInfoChar(&buf, ',');
appendStringInfoString(&buf,
(flags & FORMAT_PROC_FORCE_QUALIFY) != 0 ?
format_type_be_qualified(thisargtype) :
format_type_be(thisargtype));
}
appendStringInfoChar(&buf, ')');
result = buf.data;
ReleaseSysCache(proctup);
}
else if ((flags & FORMAT_PROC_INVALID_AS_NULL) != 0)
{
/* If object is undefined, return NULL as wanted by caller */
result = NULL;
}
else
{
/* If OID doesn't match any pg_proc entry, return it numerically */
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", procedure_oid);
}
return result;
}
/*
* Output an objname/objargs representation for the procedure with the
* given OID. If it doesn't exist, an error is thrown.
*
* This can be used to feed get_object_address.
*/
void
format_procedure_parts(Oid procedure_oid, List **objnames, List **objargs,
bool missing_ok)
{
HeapTuple proctup;
Form_pg_proc procform;
int nargs;
int i;
proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(procedure_oid));
if (!HeapTupleIsValid(proctup))
{
if (!missing_ok)
elog(ERROR, "cache lookup failed for procedure with OID %u", procedure_oid);
return;
}
procform = (Form_pg_proc) GETSTRUCT(proctup);
nargs = procform->pronargs;
*objnames = list_make2(get_namespace_name_or_temp(procform->pronamespace),
pstrdup(NameStr(procform->proname)));
*objargs = NIL;
for (i = 0; i < nargs; i++)
{
Oid thisargtype = procform->proargtypes.values[i];
*objargs = lappend(*objargs, format_type_be_qualified(thisargtype));
}
ReleaseSysCache(proctup);
}
/*
* regprocedureout - converts proc OID to "pro_name(args)"
*/
Datum
regprocedureout(PG_FUNCTION_ARGS)
{
RegProcedure proid = PG_GETARG_OID(0);
char *result;
if (proid == InvalidOid)
result = pstrdup("-");
else
result = format_procedure(proid);
PG_RETURN_CSTRING(result);
}
/*
* regprocedurerecv - converts external binary format to regprocedure
*/
Datum
regprocedurerecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regproceduresend - converts regprocedure to binary format
*/
Datum
regproceduresend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* regoperin - converts "oprname" to operator OID
*
* We also accept a numeric OID, for symmetry with the output routine.
*
* '0' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_operator entry.
*/
Datum
regoperin(PG_FUNCTION_ARGS)
{
char *opr_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
Oid result;
List *names;
FuncCandidateList clist;
int fgc_flags;
/* Handle "0" or numeric OID */
if (parseNumericOid(opr_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* Else it's a name, possibly schema-qualified */
/* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
elog(ERROR, "regoper values must be OIDs in bootstrap mode");
/*
* 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, escontext);
if (names == NIL)
PG_RETURN_NULL();
clist = OpernameGetCandidates(names, '\0', true, &fgc_flags);
if (clist == NULL)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("operator does not exist: %s", opr_name_or_oid)));
else if (clist->next != NULL)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
errmsg("more than one operator named %s",
opr_name_or_oid)));
result = clist->oid;
PG_RETURN_OID(result);
}
/*
* to_regoper - converts "oprname" to operator OID
*
* If the name is not found, we return NULL.
*/
Datum
to_regoper(PG_FUNCTION_ARGS)
{
char *opr_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
Datum result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!DirectInputFunctionCallSafe(regoperin, opr_name,
InvalidOid, -1,
(Node *) &escontext,
&result))
PG_RETURN_NULL();
PG_RETURN_DATUM(result);
}
/*
* regoperout - converts operator OID to "opr_name"
*/
Datum
regoperout(PG_FUNCTION_ARGS)
{
Oid oprid = PG_GETARG_OID(0);
char *result;
HeapTuple opertup;
if (oprid == InvalidOid)
{
result = pstrdup("0");
PG_RETURN_CSTRING(result);
}
opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(oprid));
if (HeapTupleIsValid(opertup))
{
Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
char *oprname = NameStr(operform->oprname);
/*
* In bootstrap mode, skip the fancy namespace stuff and just return
* the oper name. (This path is only needed for debugging output
* anyway.)
*/
if (IsBootstrapProcessingMode())
result = pstrdup(oprname);
else
{
FuncCandidateList clist;
int fgc_flags;
/*
* Would this oper be found (uniquely!) by regoperin? If not,
* qualify it.
*/
clist = OpernameGetCandidates(list_make1(makeString(oprname)),
'\0', false, &fgc_flags);
if (clist != NULL && clist->next == NULL &&
clist->oid == oprid)
result = pstrdup(oprname);
else
{
const char *nspname;
nspname = get_namespace_name(operform->oprnamespace);
nspname = quote_identifier(nspname);
result = (char *) palloc(strlen(nspname) + strlen(oprname) + 2);
sprintf(result, "%s.%s", nspname, oprname);
}
}
ReleaseSysCache(opertup);
}
else
{
/*
* If OID doesn't match any pg_operator entry, return it numerically
*/
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", oprid);
}
PG_RETURN_CSTRING(result);
}
/*
* regoperrecv - converts external binary format to regoper
*/
Datum
regoperrecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regopersend - converts regoper to binary format
*/
Datum
regopersend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* regoperatorin - converts "oprname(args)" to operator OID
*
* We also accept a numeric OID, for symmetry with the output routine.
*
* '0' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_operator entry.
*/
Datum
regoperatorin(PG_FUNCTION_ARGS)
{
char *opr_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
Oid result;
List *names;
int nargs;
Oid argtypes[FUNC_MAX_ARGS];
/* Handle "0" or numeric OID */
if (parseNumericOid(opr_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
elog(ERROR, "regoperator values must be OIDs in bootstrap mode");
/*
* Else it's a name and arguments. Parse the name and arguments, look up
* potential matches in the current namespace search list, and scan to see
* which one exactly matches the given argument types. (There will not be
* more than one match.)
*/
if (!parseNameAndArgTypes(opr_name_or_oid, true,
&names, &nargs, argtypes,
escontext))
PG_RETURN_NULL();
if (nargs == 1)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_PARAMETER),
errmsg("missing argument"),
errhint("Use NONE to denote the missing argument of a unary operator.")));
if (nargs != 2)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg("too many arguments"),
errhint("Provide two argument types for operator.")));
result = OpernameGetOprid(names, argtypes[0], argtypes[1]);
if (!OidIsValid(result))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("operator does not exist: %s", opr_name_or_oid)));
PG_RETURN_OID(result);
}
/*
* to_regoperator - converts "oprname(args)" to operator OID
*
* If the name is not found, we return NULL.
*/
Datum
to_regoperator(PG_FUNCTION_ARGS)
{
char *opr_name_or_oid = text_to_cstring(PG_GETARG_TEXT_PP(0));
Datum result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!DirectInputFunctionCallSafe(regoperatorin, opr_name_or_oid,
InvalidOid, -1,
(Node *) &escontext,
&result))
PG_RETURN_NULL();
PG_RETURN_DATUM(result);
}
/*
* format_operator_extended - converts operator OID to "opr_name(args)"
*
* This exports the useful functionality of regoperatorout for use
* in other backend modules. The result is a palloc'd string, or NULL.
*
* The following bits in 'flags' modify the behavior:
* - FORMAT_OPERATOR_INVALID_AS_NULL
* if the operator OID is invalid or unknown, return NULL instead
* of the numeric OID.
* - FORMAT_OPERATOR_FORCE_QUALIFY
* always schema-qualify operator names, regardless of search_path
*/
char *
format_operator_extended(Oid operator_oid, bits16 flags)
{
char *result;
HeapTuple opertup;
opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operator_oid));
if (HeapTupleIsValid(opertup))
{
Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
char *oprname = NameStr(operform->oprname);
char *nspname;
StringInfoData buf;
/* XXX no support here for bootstrap mode */
Assert(!IsBootstrapProcessingMode());
initStringInfo(&buf);
/*
* Would this oper be found (given the right args) by regoperatorin?
* If not, or if caller explicitly requests it, we need to qualify it.
*/
if ((flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ||
!OperatorIsVisible(operator_oid))
{
nspname = get_namespace_name(operform->oprnamespace);
appendStringInfo(&buf, "%s.",
quote_identifier(nspname));
}
appendStringInfo(&buf, "%s(", oprname);
if (operform->oprleft)
appendStringInfo(&buf, "%s,",
(flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ?
format_type_be_qualified(operform->oprleft) :
format_type_be(operform->oprleft));
else
appendStringInfoString(&buf, "NONE,");
if (operform->oprright)
appendStringInfo(&buf, "%s)",
(flags & FORMAT_OPERATOR_FORCE_QUALIFY) != 0 ?
format_type_be_qualified(operform->oprright) :
format_type_be(operform->oprright));
else
appendStringInfoString(&buf, "NONE)");
result = buf.data;
ReleaseSysCache(opertup);
}
else if ((flags & FORMAT_OPERATOR_INVALID_AS_NULL) != 0)
{
/* If object is undefined, return NULL as wanted by caller */
result = NULL;
}
else
{
/*
* If OID doesn't match any pg_operator entry, return it numerically
*/
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", operator_oid);
}
return result;
}
char *
format_operator(Oid operator_oid)
{
return format_operator_extended(operator_oid, 0);
}
char *
format_operator_qualified(Oid operator_oid)
{
return format_operator_extended(operator_oid,
FORMAT_OPERATOR_FORCE_QUALIFY);
}
void
format_operator_parts(Oid operator_oid, List **objnames, List **objargs,
bool missing_ok)
{
HeapTuple opertup;
Form_pg_operator oprForm;
opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operator_oid));
if (!HeapTupleIsValid(opertup))
{
if (!missing_ok)
elog(ERROR, "cache lookup failed for operator with OID %u",
operator_oid);
return;
}
oprForm = (Form_pg_operator) GETSTRUCT(opertup);
*objnames = list_make2(get_namespace_name_or_temp(oprForm->oprnamespace),
pstrdup(NameStr(oprForm->oprname)));
*objargs = NIL;
if (oprForm->oprleft)
*objargs = lappend(*objargs,
format_type_be_qualified(oprForm->oprleft));
if (oprForm->oprright)
*objargs = lappend(*objargs,
format_type_be_qualified(oprForm->oprright));
ReleaseSysCache(opertup);
}
/*
* regoperatorout - converts operator OID to "opr_name(args)"
*/
Datum
regoperatorout(PG_FUNCTION_ARGS)
{
Oid oprid = PG_GETARG_OID(0);
char *result;
if (oprid == InvalidOid)
result = pstrdup("0");
else
result = format_operator(oprid);
PG_RETURN_CSTRING(result);
}
/*
* regoperatorrecv - converts external binary format to regoperator
*/
Datum
regoperatorrecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regoperatorsend - converts regoperator to binary format
*/
Datum
regoperatorsend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* regclassin - converts "classname" to class OID
*
* We also accept a numeric OID, for symmetry with the output routine.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_class entry.
*/
Datum
regclassin(PG_FUNCTION_ARGS)
{
char *class_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
Oid result;
List *names;
/* Handle "-" or numeric OID */
if (parseDashOrOid(class_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* Else it's a name, possibly schema-qualified */
/* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
elog(ERROR, "regclass values must be OIDs in bootstrap mode");
/*
* 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, escontext);
if (names == NIL)
PG_RETURN_NULL();
/* We might not even have permissions on this relation; don't lock it. */
result = RangeVarGetRelid(makeRangeVarFromNameList(names), NoLock, true);
if (!OidIsValid(result))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation \"%s\" does not exist",
NameListToString(names))));
PG_RETURN_OID(result);
}
/*
* to_regclass - converts "classname" to class OID
*
* If the name is not found, we return NULL.
*/
Datum
to_regclass(PG_FUNCTION_ARGS)
{
char *class_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
Datum result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!DirectInputFunctionCallSafe(regclassin, class_name,
InvalidOid, -1,
(Node *) &escontext,
&result))
PG_RETURN_NULL();
PG_RETURN_DATUM(result);
}
/*
* regclassout - converts class OID to "class_name"
*/
Datum
regclassout(PG_FUNCTION_ARGS)
{
Oid classid = PG_GETARG_OID(0);
char *result;
HeapTuple classtup;
if (classid == InvalidOid)
{
result = pstrdup("-");
PG_RETURN_CSTRING(result);
}
classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(classid));
if (HeapTupleIsValid(classtup))
{
Form_pg_class classform = (Form_pg_class) GETSTRUCT(classtup);
char *classname = NameStr(classform->relname);
/*
* In bootstrap mode, skip the fancy namespace stuff and just return
* the class name. (This path is only needed for debugging output
* anyway.)
*/
if (IsBootstrapProcessingMode())
result = pstrdup(classname);
else
{
char *nspname;
/*
* Would this class be found by regclassin? If not, qualify it.
*/
if (RelationIsVisible(classid))
nspname = NULL;
else
nspname = get_namespace_name(classform->relnamespace);
result = quote_qualified_identifier(nspname, classname);
}
ReleaseSysCache(classtup);
}
else
{
/* If OID doesn't match any pg_class entry, return it numerically */
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", classid);
}
PG_RETURN_CSTRING(result);
}
/*
* regclassrecv - converts external binary format to regclass
*/
Datum
regclassrecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regclasssend - converts regclass to binary format
*/
Datum
regclasssend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* regcollationin - converts "collationname" to collation OID
*
* We also accept a numeric OID, for symmetry with the output routine.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_collation entry.
*/
Datum
regcollationin(PG_FUNCTION_ARGS)
{
char *collation_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
Oid result;
List *names;
/* Handle "-" or numeric OID */
if (parseDashOrOid(collation_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* Else it's a name, possibly schema-qualified */
/* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
elog(ERROR, "regcollation values must be OIDs in bootstrap mode");
/*
* Normal case: parse the name into components and see if it matches any
* pg_collation entries in the current search path.
*/
names = stringToQualifiedNameList(collation_name_or_oid, escontext);
if (names == NIL)
PG_RETURN_NULL();
result = get_collation_oid(names, true);
if (!OidIsValid(result))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("collation \"%s\" for encoding \"%s\" does not exist",
NameListToString(names), GetDatabaseEncodingName())));
PG_RETURN_OID(result);
}
/*
* to_regcollation - converts "collationname" to collation OID
*
* If the name is not found, we return NULL.
*/
Datum
to_regcollation(PG_FUNCTION_ARGS)
{
char *collation_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
Datum result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!DirectInputFunctionCallSafe(regcollationin, collation_name,
InvalidOid, -1,
(Node *) &escontext,
&result))
PG_RETURN_NULL();
PG_RETURN_DATUM(result);
}
/*
* regcollationout - converts collation OID to "collation_name"
*/
Datum
regcollationout(PG_FUNCTION_ARGS)
{
Oid collationid = PG_GETARG_OID(0);
char *result;
HeapTuple collationtup;
if (collationid == InvalidOid)
{
result = pstrdup("-");
PG_RETURN_CSTRING(result);
}
collationtup = SearchSysCache1(COLLOID, ObjectIdGetDatum(collationid));
if (HeapTupleIsValid(collationtup))
{
Form_pg_collation collationform = (Form_pg_collation) GETSTRUCT(collationtup);
char *collationname = NameStr(collationform->collname);
/*
* In bootstrap mode, skip the fancy namespace stuff and just return
* the collation name. (This path is only needed for debugging output
* anyway.)
*/
if (IsBootstrapProcessingMode())
result = pstrdup(collationname);
else
{
char *nspname;
/*
* Would this collation be found by regcollationin? If not,
* qualify it.
*/
if (CollationIsVisible(collationid))
nspname = NULL;
else
nspname = get_namespace_name(collationform->collnamespace);
result = quote_qualified_identifier(nspname, collationname);
}
ReleaseSysCache(collationtup);
}
else
{
/* If OID doesn't match any pg_collation entry, return it numerically */
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", collationid);
}
PG_RETURN_CSTRING(result);
}
/*
* regcollationrecv - converts external binary format to regcollation
*/
Datum
regcollationrecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regcollationsend - converts regcollation to binary format
*/
Datum
regcollationsend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* regtypein - converts "typename" to type OID
*
* The type name can be specified using the full type syntax recognized by
* the parser; for example, DOUBLE PRECISION and INTEGER[] will work and be
* translated to the correct type names. (We ignore any typmod info
* generated by the parser, however.)
*
* We also accept a numeric OID, for symmetry with the output routine,
* and for possible use in bootstrap mode.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_type entry.
*/
Datum
regtypein(PG_FUNCTION_ARGS)
{
char *typ_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
Oid result;
int32 typmod;
/* Handle "-" or numeric OID */
if (parseDashOrOid(typ_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* Else it's a type name, possibly schema-qualified or decorated */
/* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
elog(ERROR, "regtype values must be OIDs in bootstrap mode");
/*
* Normal case: invoke the full parser to deal with special cases such as
* array syntax. We don't need to check for parseTypeString failure,
* since we'll just return anyway.
*/
(void) parseTypeString(typ_name_or_oid, &result, &typmod, escontext);
PG_RETURN_OID(result);
}
/*
* to_regtype - converts "typename" to type OID
*
* If the name is not found, we return NULL.
*/
Datum
to_regtype(PG_FUNCTION_ARGS)
{
char *typ_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
Datum result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!DirectInputFunctionCallSafe(regtypein, typ_name,
InvalidOid, -1,
(Node *) &escontext,
&result))
PG_RETURN_NULL();
PG_RETURN_DATUM(result);
}
/*
* to_regtypemod - converts "typename" to type modifier
*
* If the name is not found, we return NULL.
*/
Datum
to_regtypemod(PG_FUNCTION_ARGS)
{
char *typ_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
Oid typid;
int32 typmod;
ErrorSaveContext escontext = {T_ErrorSaveContext};
/* We rely on parseTypeString to parse the input. */
if (!parseTypeString(typ_name, &typid, &typmod, (Node *) &escontext))
PG_RETURN_NULL();
PG_RETURN_INT32(typmod);
}
/*
* regtypeout - converts type OID to "typ_name"
*/
Datum
regtypeout(PG_FUNCTION_ARGS)
{
Oid typid = PG_GETARG_OID(0);
char *result;
HeapTuple typetup;
if (typid == InvalidOid)
{
result = pstrdup("-");
PG_RETURN_CSTRING(result);
}
typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
if (HeapTupleIsValid(typetup))
{
Form_pg_type typeform = (Form_pg_type) GETSTRUCT(typetup);
/*
* In bootstrap mode, skip the fancy namespace stuff and just return
* the type name. (This path is only needed for debugging output
* anyway.)
*/
if (IsBootstrapProcessingMode())
{
char *typname = NameStr(typeform->typname);
result = pstrdup(typname);
}
else
result = format_type_be(typid);
ReleaseSysCache(typetup);
}
else
{
/* If OID doesn't match any pg_type entry, return it numerically */
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", typid);
}
PG_RETURN_CSTRING(result);
}
/*
* regtyperecv - converts external binary format to regtype
*/
Datum
regtyperecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regtypesend - converts regtype to binary format
*/
Datum
regtypesend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* regconfigin - converts "tsconfigname" to tsconfig OID
*
* We also accept a numeric OID, for symmetry with the output routine.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_ts_config entry.
*/
Datum
regconfigin(PG_FUNCTION_ARGS)
{
char *cfg_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
Oid result;
List *names;
/* Handle "-" or numeric OID */
if (parseDashOrOid(cfg_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
elog(ERROR, "regconfig values must be OIDs in bootstrap mode");
/*
* Normal case: parse the name into components and see if it matches any
* pg_ts_config entries in the current search path.
*/
names = stringToQualifiedNameList(cfg_name_or_oid, escontext);
if (names == NIL)
PG_RETURN_NULL();
result = get_ts_config_oid(names, true);
if (!OidIsValid(result))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search configuration \"%s\" does not exist",
NameListToString(names))));
PG_RETURN_OID(result);
}
/*
* regconfigout - converts tsconfig OID to "tsconfigname"
*/
Datum
regconfigout(PG_FUNCTION_ARGS)
{
Oid cfgid = PG_GETARG_OID(0);
char *result;
HeapTuple cfgtup;
if (cfgid == InvalidOid)
{
result = pstrdup("-");
PG_RETURN_CSTRING(result);
}
cfgtup = SearchSysCache1(TSCONFIGOID, ObjectIdGetDatum(cfgid));
if (HeapTupleIsValid(cfgtup))
{
Form_pg_ts_config cfgform = (Form_pg_ts_config) GETSTRUCT(cfgtup);
char *cfgname = NameStr(cfgform->cfgname);
char *nspname;
/*
* Would this config be found by regconfigin? If not, qualify it.
*/
if (TSConfigIsVisible(cfgid))
nspname = NULL;
else
nspname = get_namespace_name(cfgform->cfgnamespace);
result = quote_qualified_identifier(nspname, cfgname);
ReleaseSysCache(cfgtup);
}
else
{
/* If OID doesn't match any pg_ts_config row, return it numerically */
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", cfgid);
}
PG_RETURN_CSTRING(result);
}
/*
* regconfigrecv - converts external binary format to regconfig
*/
Datum
regconfigrecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regconfigsend - converts regconfig to binary format
*/
Datum
regconfigsend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* regdictionaryin - converts "tsdictionaryname" to tsdictionary OID
*
* We also accept a numeric OID, for symmetry with the output routine.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_ts_dict entry.
*/
Datum
regdictionaryin(PG_FUNCTION_ARGS)
{
char *dict_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
Oid result;
List *names;
/* Handle "-" or numeric OID */
if (parseDashOrOid(dict_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
elog(ERROR, "regdictionary values must be OIDs in bootstrap mode");
/*
* Normal case: parse the name into components and see if it matches any
* pg_ts_dict entries in the current search path.
*/
names = stringToQualifiedNameList(dict_name_or_oid, escontext);
if (names == NIL)
PG_RETURN_NULL();
result = get_ts_dict_oid(names, true);
if (!OidIsValid(result))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("text search dictionary \"%s\" does not exist",
NameListToString(names))));
PG_RETURN_OID(result);
}
/*
* regdictionaryout - converts tsdictionary OID to "tsdictionaryname"
*/
Datum
regdictionaryout(PG_FUNCTION_ARGS)
{
Oid dictid = PG_GETARG_OID(0);
char *result;
HeapTuple dicttup;
if (dictid == InvalidOid)
{
result = pstrdup("-");
PG_RETURN_CSTRING(result);
}
dicttup = SearchSysCache1(TSDICTOID, ObjectIdGetDatum(dictid));
if (HeapTupleIsValid(dicttup))
{
Form_pg_ts_dict dictform = (Form_pg_ts_dict) GETSTRUCT(dicttup);
char *dictname = NameStr(dictform->dictname);
char *nspname;
/*
* Would this dictionary be found by regdictionaryin? If not, qualify
* it.
*/
if (TSDictionaryIsVisible(dictid))
nspname = NULL;
else
nspname = get_namespace_name(dictform->dictnamespace);
result = quote_qualified_identifier(nspname, dictname);
ReleaseSysCache(dicttup);
}
else
{
/* If OID doesn't match any pg_ts_dict row, return it numerically */
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", dictid);
}
PG_RETURN_CSTRING(result);
}
/*
* regdictionaryrecv - converts external binary format to regdictionary
*/
Datum
regdictionaryrecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regdictionarysend - converts regdictionary to binary format
*/
Datum
regdictionarysend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* regrolein - converts "rolename" to role OID
*
* We also accept a numeric OID, for symmetry with the output routine.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_authid entry.
*/
Datum
regrolein(PG_FUNCTION_ARGS)
{
char *role_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
Oid result;
List *names;
/* Handle "-" or numeric OID */
if (parseDashOrOid(role_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
elog(ERROR, "regrole values must be OIDs in bootstrap mode");
/* Normal case: see if the name matches any pg_authid entry. */
names = stringToQualifiedNameList(role_name_or_oid, escontext);
if (names == NIL)
PG_RETURN_NULL();
if (list_length(names) != 1)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_NAME),
errmsg("invalid name syntax")));
result = get_role_oid(strVal(linitial(names)), true);
if (!OidIsValid(result))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("role \"%s\" does not exist",
strVal(linitial(names)))));
PG_RETURN_OID(result);
}
/*
* to_regrole - converts "rolename" to role OID
*
* If the name is not found, we return NULL.
*/
Datum
to_regrole(PG_FUNCTION_ARGS)
{
char *role_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
Datum result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!DirectInputFunctionCallSafe(regrolein, role_name,
InvalidOid, -1,
(Node *) &escontext,
&result))
PG_RETURN_NULL();
PG_RETURN_DATUM(result);
}
/*
* regroleout - converts role OID to "role_name"
*/
Datum
regroleout(PG_FUNCTION_ARGS)
{
Oid roleoid = PG_GETARG_OID(0);
char *result;
if (roleoid == InvalidOid)
{
result = pstrdup("-");
PG_RETURN_CSTRING(result);
}
result = GetUserNameFromId(roleoid, true);
if (result)
{
/* pstrdup is not really necessary, but it avoids a compiler warning */
result = pstrdup(quote_identifier(result));
}
else
{
/* If OID doesn't match any role, return it numerically */
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", roleoid);
}
PG_RETURN_CSTRING(result);
}
/*
* regrolerecv - converts external binary format to regrole
*/
Datum
regrolerecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regrolesend - converts regrole to binary format
*/
Datum
regrolesend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* regnamespacein - converts "nspname" to namespace OID
*
* We also accept a numeric OID, for symmetry with the output routine.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_namespace entry.
*/
Datum
regnamespacein(PG_FUNCTION_ARGS)
{
char *nsp_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
Oid result;
List *names;
/* Handle "-" or numeric OID */
if (parseDashOrOid(nsp_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
elog(ERROR, "regnamespace values must be OIDs in bootstrap mode");
/* Normal case: see if the name matches any pg_namespace entry. */
names = stringToQualifiedNameList(nsp_name_or_oid, escontext);
if (names == NIL)
PG_RETURN_NULL();
if (list_length(names) != 1)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_NAME),
errmsg("invalid name syntax")));
result = get_namespace_oid(strVal(linitial(names)), true);
if (!OidIsValid(result))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema \"%s\" does not exist",
strVal(linitial(names)))));
PG_RETURN_OID(result);
}
/*
* to_regnamespace - converts "nspname" to namespace OID
*
* If the name is not found, we return NULL.
*/
Datum
to_regnamespace(PG_FUNCTION_ARGS)
{
char *nsp_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
Datum result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!DirectInputFunctionCallSafe(regnamespacein, nsp_name,
InvalidOid, -1,
(Node *) &escontext,
&result))
PG_RETURN_NULL();
PG_RETURN_DATUM(result);
}
/*
* regnamespaceout - converts namespace OID to "nsp_name"
*/
Datum
regnamespaceout(PG_FUNCTION_ARGS)
{
Oid nspid = PG_GETARG_OID(0);
char *result;
if (nspid == InvalidOid)
{
result = pstrdup("-");
PG_RETURN_CSTRING(result);
}
result = get_namespace_name(nspid);
if (result)
{
/* pstrdup is not really necessary, but it avoids a compiler warning */
result = pstrdup(quote_identifier(result));
}
else
{
/* If OID doesn't match any namespace, return it numerically */
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", nspid);
}
PG_RETURN_CSTRING(result);
}
/*
* regnamespacerecv - converts external binary format to regnamespace
*/
Datum
regnamespacerecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regnamespacesend - converts regnamespace to binary format
*/
Datum
regnamespacesend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* regdatabasein - converts database name to database OID
*
* We also accept a numeric OID, for symmetry with the output routine.
*
* '-' signifies unknown (OID 0). In all other cases, the input must
* match an existing pg_database entry.
*/
Datum
regdatabasein(PG_FUNCTION_ARGS)
{
char *db_name_or_oid = PG_GETARG_CSTRING(0);
Node *escontext = fcinfo->context;
Oid result;
List *names;
/* Handle "-" or numeric OID */
if (parseDashOrOid(db_name_or_oid, &result, escontext))
PG_RETURN_OID(result);
/* The rest of this wouldn't work in bootstrap mode */
if (IsBootstrapProcessingMode())
elog(ERROR, "regdatabase values must be OIDs in bootstrap mode");
/* Normal case: see if the name matches any pg_database entry. */
names = stringToQualifiedNameList(db_name_or_oid, escontext);
if (names == NIL)
PG_RETURN_NULL();
if (list_length(names) != 1)
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_INVALID_NAME),
errmsg("invalid name syntax")));
result = get_database_oid(strVal(linitial(names)), true);
if (!OidIsValid(result))
ereturn(escontext, (Datum) 0,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("database \"%s\" does not exist",
strVal(linitial(names)))));
PG_RETURN_OID(result);
}
/*
* to_regdatabase - converts database name to database OID
*
* If the name is not found, we return NULL.
*/
Datum
to_regdatabase(PG_FUNCTION_ARGS)
{
char *db_name = text_to_cstring(PG_GETARG_TEXT_PP(0));
Datum result;
ErrorSaveContext escontext = {T_ErrorSaveContext};
if (!DirectInputFunctionCallSafe(regdatabasein, db_name,
InvalidOid, -1,
(Node *) &escontext,
&result))
PG_RETURN_NULL();
PG_RETURN_DATUM(result);
}
/*
* regdatabaseout - converts database OID to database name
*/
Datum
regdatabaseout(PG_FUNCTION_ARGS)
{
Oid dboid = PG_GETARG_OID(0);
char *result;
if (dboid == InvalidOid)
{
result = pstrdup("-");
PG_RETURN_CSTRING(result);
}
result = get_database_name(dboid);
if (result)
{
/* pstrdup is not really necessary, but it avoids a compiler warning */
result = pstrdup(quote_identifier(result));
}
else
{
/* If OID doesn't match any database, return it numerically */
result = (char *) palloc(NAMEDATALEN);
snprintf(result, NAMEDATALEN, "%u", dboid);
}
PG_RETURN_CSTRING(result);
}
/*
* regdatabaserecv - converts external binary format to regdatabase
*/
Datum
regdatabaserecv(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidrecv, so share code */
return oidrecv(fcinfo);
}
/*
* regdatabasesend - converts regdatabase to binary format
*/
Datum
regdatabasesend(PG_FUNCTION_ARGS)
{
/* Exactly the same as oidsend, so share code */
return oidsend(fcinfo);
}
/*
* text_regclass: convert text to regclass
*
* This could be replaced by CoerceViaIO, except that we need to treat
* text-to-regclass as an implicit cast to support legacy forms of nextval()
* and related functions.
*/
Datum
text_regclass(PG_FUNCTION_ARGS)
{
text *relname = PG_GETARG_TEXT_PP(0);
Oid result;
RangeVar *rv;
rv = makeRangeVarFromNameList(textToQualifiedNameList(relname));
/* We might not even have permissions on this relation; don't lock it. */
result = RangeVarGetRelid(rv, NoLock, false);
PG_RETURN_OID(result);
}
/*
* Given a C string, parse it into a qualified-name list.
*
* If escontext is an ErrorSaveContext node, invalid input will be
* reported there instead of being thrown, and we return NIL.
* (NIL is not possible as a success return, since empty-input is an error.)
*/
List *
stringToQualifiedNameList(const char *string, Node *escontext)
{
char *rawname;
List *result = NIL;
List *namelist;
ListCell *l;
/* We need a modifiable copy of the input string. */
rawname = pstrdup(string);
if (!SplitIdentifierString(rawname, '.', &namelist))
ereturn(escontext, NIL,
(errcode(ERRCODE_INVALID_NAME),
errmsg("invalid name syntax")));
if (namelist == NIL)
ereturn(escontext, NIL,
(errcode(ERRCODE_INVALID_NAME),
errmsg("invalid name syntax")));
foreach(l, namelist)
{
char *curname = (char *) lfirst(l);
result = lappend(result, makeString(pstrdup(curname)));
}
pfree(rawname);
list_free(namelist);
return result;
}
/*****************************************************************************
* SUPPORT ROUTINES *
*****************************************************************************/
/*
* Given a C string, see if it is all-digits (and not empty).
* If so, convert directly to OID and return true.
* If it is not all-digits, return false.
*
* If escontext is an ErrorSaveContext node, any error in oidin() will be
* reported there instead of being thrown (but we still return true).
*/
static bool
parseNumericOid(char *string, Oid *result, Node *escontext)
{
if (string[0] >= '0' && string[0] <= '9' &&
strspn(string, "0123456789") == strlen(string))
{
Datum oid_datum;
/* We need not care here whether oidin() fails or not. */
(void) DirectInputFunctionCallSafe(oidin, string,
InvalidOid, -1,
escontext,
&oid_datum);
*result = DatumGetObjectId(oid_datum);
return true;
}
/* Prevent uninitialized-variable warnings from stupider compilers. */
*result = InvalidOid;
return false;
}
/*
* As above, but also accept "-" as meaning 0 (InvalidOid).
*/
static bool
parseDashOrOid(char *string, Oid *result, Node *escontext)
{
/* '-' ? */
if (strcmp(string, "-") == 0)
{
*result = InvalidOid;
return true;
}
/* Numeric OID? */
return parseNumericOid(string, result, escontext);
}
/*
* 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;
* the argtypes array should be of size FUNC_MAX_ARGS). The function or
* operator name is returned to *names as a List of Strings.
*
* If allowNone is true, accept "NONE" and return it as InvalidOid (this is
* for unary operators).
*
* Returns true on success, false on failure (the latter only possible
* if escontext is an ErrorSaveContext node).
*/
static bool
parseNameAndArgTypes(const char *string, bool allowNone, List **names,
int *nargs, Oid *argtypes,
Node *escontext)
{
char *rawname;
char *ptr;
char *ptr2;
char *typename;
bool in_quote;
bool had_comma;
int paren_count;
Oid typeid;
int32 typmod;
/* We need a modifiable copy of the input string. */
rawname = pstrdup(string);
/* Scan to find the expected left paren; mustn't be quoted */
in_quote = false;
for (ptr = rawname; *ptr; ptr++)
{
if (*ptr == '"')
in_quote = !in_quote;
else if (*ptr == '(' && !in_quote)
break;
}
if (*ptr == '\0')
ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("expected a left parenthesis")));
/* Separate the name and parse it into a list */
*ptr++ = '\0';
*names = stringToQualifiedNameList(rawname, escontext);
if (*names == NIL)
return false;
/* Check for the trailing right parenthesis and remove it */
ptr2 = ptr + strlen(ptr);
while (--ptr2 > ptr)
{
if (!scanner_isspace(*ptr2))
break;
}
if (*ptr2 != ')')
ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("expected a right parenthesis")));
*ptr2 = '\0';
/* Separate the remaining string into comma-separated type names */
*nargs = 0;
had_comma = false;
for (;;)
{
/* allow leading whitespace */
while (scanner_isspace(*ptr))
ptr++;
if (*ptr == '\0')
{
/* End of string. Okay unless we had a comma before. */
if (had_comma)
ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("expected a type name")));
break;
}
typename = ptr;
/* Find end of type name --- end of string or comma */
/* ... but not a quoted or parenthesized comma */
in_quote = false;
paren_count = 0;
for (; *ptr; ptr++)
{
if (*ptr == '"')
in_quote = !in_quote;
else if (*ptr == ',' && !in_quote && paren_count == 0)
break;
else if (!in_quote)
{
switch (*ptr)
{
case '(':
case '[':
paren_count++;
break;
case ')':
case ']':
paren_count--;
break;
}
}
}
if (in_quote || paren_count != 0)
ereturn(escontext, false,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("improper type name")));
ptr2 = ptr;
if (*ptr == ',')
{
had_comma = true;
*ptr++ = '\0';
}
else
{
had_comma = false;
Assert(*ptr == '\0');
}
/* Lop off trailing whitespace */
while (--ptr2 >= typename)
{
if (!scanner_isspace(*ptr2))
break;
*ptr2 = '\0';
}
if (allowNone && pg_strcasecmp(typename, "none") == 0)
{
/* Special case for NONE */
typeid = InvalidOid;
typmod = -1;
}
else
{
/* Use full parser to resolve the type name */
if (!parseTypeString(typename, &typeid, &typmod, escontext))
return false;
}
if (*nargs >= FUNC_MAX_ARGS)
ereturn(escontext, false,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg("too many arguments")));
argtypes[*nargs] = typeid;
(*nargs)++;
}
pfree(rawname);
return true;
}