| /*------------------------------------------------------------------------- |
| * |
| * 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 <ctype.h> |
| |
| #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; |
| } |