/*------------------------------------------------------------------------- * * 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-2023, 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 #include "access/htup_details.h" #include "catalog/namespace.h" #include "catalog/pg_class.h" #include "catalog/pg_collation.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; /* 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); 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; /* * Would this proc be found (uniquely!) by regprocin? If not, * qualify it. */ clist = FuncnameGetCandidates(list_make1(makeString(proname)), -1, NIL, false, false, false, false); 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; /* 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); 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; /* 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); 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; /* * Would this oper be found (uniquely!) by regoperin? If not, * qualify it. */ clist = OpernameGetCandidates(list_make1(makeString(oprname)), '\0', false); 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); } /* * 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); } /* * 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; }