mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
two small changes to the API since last patch, which hopefully completes the decoupling of composite function support from SRF specific support. Joe Conway
1135 lines
28 KiB
C
1135 lines
28 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* regproc.c
|
|
* Functions for the built-in types regproc, regclass, regtype, etc.
|
|
*
|
|
* These types are all binary-compatible with type Oid, and rely on Oid
|
|
* for comparison and so forth. Their only interesting behavior is in
|
|
* special I/O conversion routines.
|
|
*
|
|
*
|
|
* Portions Copyright (c) 1996-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.69 2002/06/20 17:19:08 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
|
|
#include <ctype.h>
|
|
|
|
#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 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);
|
|
}
|
|
|
|
|
|
/*
|
|
* Given a C string, parse it into a qualified-name list.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/*****************************************************************************
|
|
* SUPPORT ROUTINES *
|
|
*****************************************************************************/
|
|
|
|
/*
|
|
* 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);
|
|
}
|