/*------------------------------------------------------------------------- * * 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-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/utils/adt/regproc.c,v 1.68 2002/05/11 00:24:16 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include #include "access/genam.h" #include "access/heapam.h" #include "catalog/catname.h" #include "catalog/indexing.h" #include "catalog/namespace.h" #include "catalog/pg_operator.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "lib/stringinfo.h" #include "miscadmin.h" #include "parser/parse_type.h" #include "utils/builtins.h" #include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/syscache.h" static List *stringToQualifiedNameList(const char *string, const char *caller); static void parseNameAndArgTypes(const char *string, const char *caller, const char *type0_spelling, List **names, int *nargs, Oid *argtypes); /***************************************************************************** * USER I/O ROUTINES * *****************************************************************************/ /* * regprocin - converts "proname" to proc OID * * We also accept a numeric OID, mostly for historical reasons. * * '-' 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); RegProcedure result = InvalidOid; List *names; FuncCandidateList clist; /* '-' ? */ if (strcmp(pro_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (pro_name_or_oid[0] >= '0' && pro_name_or_oid[0] <= '9' && strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid)) { Oid searchOid; searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(pro_name_or_oid))); result = (RegProcedure) GetSysCacheOid(PROCOID, ObjectIdGetDatum(searchOid), 0, 0, 0); if (!RegProcedureIsValid(result)) elog(ERROR, "No procedure with oid %s", pro_name_or_oid); PG_RETURN_OID(result); } /* Else it's a name, possibly schema-qualified */ /* * In bootstrap mode we assume the given name is not schema-qualified, * and just search pg_proc for a unique match. This is needed for * initializing other system catalogs (pg_namespace may not exist yet, * and certainly there are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { int matches = 0; Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyEntryInitialize(&skey[0], 0x0, (AttrNumber) Anum_pg_proc_proname, (RegProcedure) F_NAMEEQ, CStringGetDatum(pro_name_or_oid)); hdesc = heap_openr(ProcedureRelationName, AccessShareLock); sysscan = systable_beginscan(hdesc, ProcedureNameNspIndex, true, SnapshotNow, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { result = (RegProcedure) tuple->t_data->t_oid; if (++matches > 1) break; } systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); if (matches == 0) elog(ERROR, "No procedure with name %s", pro_name_or_oid); else if (matches > 1) elog(ERROR, "There is more than one procedure named %s", pro_name_or_oid); PG_RETURN_OID(result); } /* * Normal case: parse the name into components and see if it * matches any pg_proc entries in the current search path. */ names = stringToQualifiedNameList(pro_name_or_oid, "regprocin"); clist = FuncnameGetCandidates(names, -1); if (clist == NULL) elog(ERROR, "No procedure with name %s", pro_name_or_oid); else if (clist->next != NULL) elog(ERROR, "There is more than one procedure named %s", pro_name_or_oid); result = clist->oid; PG_RETURN_OID(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 = SearchSysCache(PROCOID, ObjectIdGetDatum(proid), 0, 0, 0); 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(makeList1(makeString(proname)), -1); 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); } /* * regprocedurein - converts "proname(args)" to proc OID * * We also accept a numeric OID, mostly for historical reasons. * * '-' 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); RegProcedure result = InvalidOid; List *names; int nargs; Oid argtypes[FUNC_MAX_ARGS]; FuncCandidateList clist; /* '-' ? */ if (strcmp(pro_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (pro_name_or_oid[0] >= '0' && pro_name_or_oid[0] <= '9' && strspn(pro_name_or_oid, "0123456789") == strlen(pro_name_or_oid)) { Oid searchOid; searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(pro_name_or_oid))); result = (RegProcedure) GetSysCacheOid(PROCOID, ObjectIdGetDatum(searchOid), 0, 0, 0); if (!RegProcedureIsValid(result)) elog(ERROR, "No procedure with oid %s", pro_name_or_oid); PG_RETURN_OID(result); } /* * 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.) * * XXX at present, this code will not work in bootstrap mode, hence this * datatype cannot be used for any system column that needs to receive * data during bootstrap. */ parseNameAndArgTypes(pro_name_or_oid, "regprocedurein", "opaque", &names, &nargs, argtypes); clist = FuncnameGetCandidates(names, nargs); for (; clist; clist = clist->next) { if (memcmp(clist->args, argtypes, nargs * sizeof(Oid)) == 0) break; } if (clist == NULL) elog(ERROR, "No procedure with name %s", pro_name_or_oid); result = clist->oid; PG_RETURN_OID(result); } /* * regprocedureout - converts proc OID to "pro_name(args)" */ Datum regprocedureout(PG_FUNCTION_ARGS) { RegProcedure proid = PG_GETARG_OID(0); char *result; HeapTuple proctup; if (proid == InvalidOid) { result = pstrdup("-"); PG_RETURN_CSTRING(result); } proctup = SearchSysCache(PROCOID, ObjectIdGetDatum(proid), 0, 0, 0); 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 */ initStringInfo(&buf); /* * Would this proc be found (given the right args) by regprocedurein? * If not, we need to qualify it. */ if (FunctionIsVisible(proid)) 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[i]; if (i > 0) appendStringInfoChar(&buf, ','); if (OidIsValid(thisargtype)) appendStringInfo(&buf, "%s", format_type_be(thisargtype)); else appendStringInfo(&buf, "opaque"); } appendStringInfoChar(&buf, ')'); result = buf.data; 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); } /* * regoperin - converts "oprname" to operator OID * * We also accept a numeric OID, mostly for historical reasons. * * '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); Oid result = InvalidOid; List *names; FuncCandidateList clist; /* '0' ? */ if (strcmp(opr_name_or_oid, "0") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (opr_name_or_oid[0] >= '0' && opr_name_or_oid[0] <= '9' && strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid)) { Oid searchOid; searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(opr_name_or_oid))); result = GetSysCacheOid(OPEROID, ObjectIdGetDatum(searchOid), 0, 0, 0); if (!OidIsValid(result)) elog(ERROR, "No operator with oid %s", opr_name_or_oid); PG_RETURN_OID(result); } /* Else it's a name, possibly schema-qualified */ /* * In bootstrap mode we assume the given name is not schema-qualified, * and just search pg_operator for a unique match. This is needed for * initializing other system catalogs (pg_namespace may not exist yet, * and certainly there are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { int matches = 0; Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyEntryInitialize(&skey[0], 0x0, (AttrNumber) Anum_pg_operator_oprname, (RegProcedure) F_NAMEEQ, CStringGetDatum(opr_name_or_oid)); hdesc = heap_openr(OperatorRelationName, AccessShareLock); sysscan = systable_beginscan(hdesc, OperatorNameNspIndex, true, SnapshotNow, 1, skey); while (HeapTupleIsValid(tuple = systable_getnext(sysscan))) { result = tuple->t_data->t_oid; if (++matches > 1) break; } systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); if (matches == 0) elog(ERROR, "No operator with name %s", opr_name_or_oid); else if (matches > 1) elog(ERROR, "There is more than one operator named %s", opr_name_or_oid); PG_RETURN_OID(result); } /* * Normal case: parse the name into components and see if it * matches any pg_operator entries in the current search path. */ names = stringToQualifiedNameList(opr_name_or_oid, "regoperin"); clist = OpernameGetCandidates(names, '\0'); if (clist == NULL) elog(ERROR, "No operator with name %s", opr_name_or_oid); else if (clist->next != NULL) elog(ERROR, "There is more than one operator named %s", opr_name_or_oid); result = clist->oid; PG_RETURN_OID(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 = SearchSysCache(OPEROID, ObjectIdGetDatum(oprid), 0, 0, 0); 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(makeList1(makeString(oprname)), '\0'); 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); } /* * regoperatorin - converts "oprname(args)" to operator OID * * We also accept a numeric OID, mostly for historical reasons. * * '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); Oid result = InvalidOid; List *names; int nargs; Oid argtypes[FUNC_MAX_ARGS]; char oprkind; FuncCandidateList clist; /* '0' ? */ if (strcmp(opr_name_or_oid, "0") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (opr_name_or_oid[0] >= '0' && opr_name_or_oid[0] <= '9' && strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid)) { Oid searchOid; searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(opr_name_or_oid))); result = GetSysCacheOid(OPEROID, ObjectIdGetDatum(searchOid), 0, 0, 0); if (!OidIsValid(result)) elog(ERROR, "No operator with oid %s", opr_name_or_oid); PG_RETURN_OID(result); } /* * 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.) * * XXX at present, this code will not work in bootstrap mode, hence this * datatype cannot be used for any system column that needs to receive * data during bootstrap. */ parseNameAndArgTypes(opr_name_or_oid, "regoperatorin", "none", &names, &nargs, argtypes); if (nargs == 1) elog(ERROR, "regoperatorin: use NONE to denote the missing argument of a unary operator"); if (nargs != 2) elog(ERROR, "regoperatorin: provide two argument types for operator"); if (argtypes[0] == InvalidOid) oprkind = 'l'; else if (argtypes[1] == InvalidOid) oprkind = 'r'; else oprkind = 'b'; clist = OpernameGetCandidates(names, oprkind); for (; clist; clist = clist->next) { if (memcmp(clist->args, argtypes, 2 * sizeof(Oid)) == 0) break; } if (clist == NULL) elog(ERROR, "No operator with name %s", opr_name_or_oid); result = clist->oid; PG_RETURN_OID(result); } /* * regoperatorout - converts operator OID to "opr_name(args)" */ Datum regoperatorout(PG_FUNCTION_ARGS) { Oid oprid = PG_GETARG_OID(0); char *result; HeapTuple opertup; if (oprid == InvalidOid) { result = pstrdup("0"); PG_RETURN_CSTRING(result); } opertup = SearchSysCache(OPEROID, ObjectIdGetDatum(oprid), 0, 0, 0); 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 */ initStringInfo(&buf); /* * Would this oper be found (given the right args) by regoperatorin? * If not, we need to qualify it. */ if (!OperatorIsVisible(oprid)) { nspname = get_namespace_name(operform->oprnamespace); appendStringInfo(&buf, "%s.", quote_identifier(nspname)); } appendStringInfo(&buf, "%s(", oprname); if (operform->oprleft) appendStringInfo(&buf, "%s,", format_type_be(operform->oprleft)); else appendStringInfo(&buf, "NONE,"); if (operform->oprright) appendStringInfo(&buf, "%s)", format_type_be(operform->oprright)); else appendStringInfo(&buf, "NONE)"); result = buf.data; 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); } /* * regclassin - converts "classname" to class OID * * We also accept a numeric OID, mostly for historical reasons. * * '-' 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); Oid result = InvalidOid; List *names; /* '-' ? */ if (strcmp(class_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (class_name_or_oid[0] >= '0' && class_name_or_oid[0] <= '9' && strspn(class_name_or_oid, "0123456789") == strlen(class_name_or_oid)) { Oid searchOid; searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(class_name_or_oid))); result = GetSysCacheOid(RELOID, ObjectIdGetDatum(searchOid), 0, 0, 0); if (!OidIsValid(result)) elog(ERROR, "No class with oid %s", class_name_or_oid); PG_RETURN_OID(result); } /* Else it's a name, possibly schema-qualified */ /* * In bootstrap mode we assume the given name is not schema-qualified, * and just search pg_class for a match. This is needed for * initializing other system catalogs (pg_namespace may not exist yet, * and certainly there are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyEntryInitialize(&skey[0], 0x0, (AttrNumber) Anum_pg_class_relname, (RegProcedure) F_NAMEEQ, CStringGetDatum(class_name_or_oid)); hdesc = heap_openr(RelationRelationName, AccessShareLock); sysscan = systable_beginscan(hdesc, ClassNameNspIndex, true, SnapshotNow, 1, skey); if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) result = tuple->t_data->t_oid; else elog(ERROR, "No class with name %s", class_name_or_oid); /* We assume there can be only one match */ systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); PG_RETURN_OID(result); } /* * Normal case: parse the name into components and see if it * matches any pg_class entries in the current search path. */ names = stringToQualifiedNameList(class_name_or_oid, "regclassin"); result = RangeVarGetRelid(makeRangeVarFromNameList(names), false); PG_RETURN_OID(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 = SearchSysCache(RELOID, ObjectIdGetDatum(classid), 0, 0, 0); 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); } /* * regtypein - converts "typename" to type OID * * We also accept a numeric OID, mostly for historical reasons. * * '-' signifies unknown (OID 0). In all other cases, the input must * match an existing pg_type entry. * * In bootstrap mode the name must just equal some existing name in pg_type. * In normal mode 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.) */ Datum regtypein(PG_FUNCTION_ARGS) { char *typ_name_or_oid = PG_GETARG_CSTRING(0); Oid result = InvalidOid; int32 typmod; /* '-' ? */ if (strcmp(typ_name_or_oid, "-") == 0) PG_RETURN_OID(InvalidOid); /* Numeric OID? */ if (typ_name_or_oid[0] >= '0' && typ_name_or_oid[0] <= '9' && strspn(typ_name_or_oid, "0123456789") == strlen(typ_name_or_oid)) { Oid searchOid; searchOid = DatumGetObjectId(DirectFunctionCall1(oidin, CStringGetDatum(typ_name_or_oid))); result = GetSysCacheOid(TYPEOID, ObjectIdGetDatum(searchOid), 0, 0, 0); if (!OidIsValid(result)) elog(ERROR, "No type with oid %s", typ_name_or_oid); PG_RETURN_OID(result); } /* Else it's a type name, possibly schema-qualified or decorated */ /* * In bootstrap mode we assume the given name is not schema-qualified, * and just search pg_type for a match. This is needed for * initializing other system catalogs (pg_namespace may not exist yet, * and certainly there are no schemas other than pg_catalog). */ if (IsBootstrapProcessingMode()) { Relation hdesc; ScanKeyData skey[1]; SysScanDesc sysscan; HeapTuple tuple; ScanKeyEntryInitialize(&skey[0], 0x0, (AttrNumber) Anum_pg_type_typname, (RegProcedure) F_NAMEEQ, CStringGetDatum(typ_name_or_oid)); hdesc = heap_openr(TypeRelationName, AccessShareLock); sysscan = systable_beginscan(hdesc, TypeNameNspIndex, true, SnapshotNow, 1, skey); if (HeapTupleIsValid(tuple = systable_getnext(sysscan))) result = tuple->t_data->t_oid; else elog(ERROR, "No type with name %s", typ_name_or_oid); /* We assume there can be only one match */ systable_endscan(sysscan); heap_close(hdesc, AccessShareLock); PG_RETURN_OID(result); } /* * Normal case: invoke the full parser to deal with special cases * such as array syntax. */ parseTypeString(typ_name_or_oid, &result, &typmod); PG_RETURN_OID(result); } /* * 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 = SearchSysCache(TYPEOID, ObjectIdGetDatum(typid), 0, 0, 0); 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); } /***************************************************************************** * SUPPORT ROUTINES * *****************************************************************************/ /* * Given a C string, parse it into a qualified-name list. */ static List * stringToQualifiedNameList(const char *string, const char *caller) { char *rawname; List *result = NIL; List *namelist; List *l; /* We need a modifiable copy of the input string. */ rawname = pstrdup(string); if (!SplitIdentifierString(rawname, '.', &namelist)) elog(ERROR, "%s: invalid name syntax", caller); if (namelist == NIL) elog(ERROR, "%s: invalid name syntax", caller); foreach(l, namelist) { char *curname = (char *) lfirst(l); result = lappend(result, makeString(pstrdup(curname))); } pfree(rawname); freeList(namelist); return result; } /* * 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 type0_spelling is not NULL, it is a name to be accepted as a * placeholder for OID 0. */ static void parseNameAndArgTypes(const char *string, const char *caller, const char *type0_spelling, List **names, int *nargs, Oid *argtypes) { 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') elog(ERROR, "%s: expected a left parenthesis", caller); /* Separate the name and parse it into a list */ *ptr++ = '\0'; *names = stringToQualifiedNameList(rawname, caller); /* Check for the trailing right parenthesis and remove it */ ptr2 = ptr + strlen(ptr); while (--ptr2 > ptr) { if (!isspace((unsigned char) *ptr2)) break; } if (*ptr2 != ')') elog(ERROR, "%s: expected a right parenthesis", caller); *ptr2 = '\0'; /* Separate the remaining string into comma-separated type names */ *nargs = 0; had_comma = false; for (;;) { /* allow leading whitespace */ while (isspace((unsigned char) *ptr)) ptr++; if (*ptr == '\0') { /* End of string. Okay unless we had a comma before. */ if (had_comma) elog(ERROR, "%s: expected a type name", caller); 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) elog(ERROR, "%s: improper type name", caller); ptr2 = ptr; if (*ptr == ',') { had_comma = true; *ptr++ = '\0'; } else { had_comma = false; Assert(*ptr == '\0'); } /* Lop off trailing whitespace */ while (--ptr2 >= typename) { if (!isspace((unsigned char) *ptr2)) break; *ptr2 = '\0'; } if (type0_spelling && strcasecmp(typename, type0_spelling) == 0) { /* Special case for OPAQUE or NONE */ typeid = InvalidOid; typmod = -1; } else { /* Use full parser to resolve the type name */ parseTypeString(typename, &typeid, &typmod); } if (*nargs >= FUNC_MAX_ARGS) elog(ERROR, "%s: too many argument datatypes", caller); argtypes[*nargs] = typeid; (*nargs)++; } pfree(rawname); }